Doubbox 入门学习

2024-08-06 14:13:12 浏览数 (2)

Doubbox 入门

官方网站

Apache Dubbo

前言

  • Dubbox是一个分布式服务框架
  • 其前身是阿里巴巴开源项目Dubbo,被国内电商及互联网项目中使用, 后期阿里巴巴停止了Dubbo项目的维护(2018年 Dubbo已捐献给Apache基金会)
  • 因为 阿里巴巴内部的HSF框架比Dubbo更高效,更贴合他们业务。 当当网便在Dubbo基础上进行优化,并继续进行维护,为了与原有的Dubbo区别,故将其命名为 Dubbox

Dubbox作用

  • Dubbox 致力于提供高性能和透明化的RPC远程服务调用方案 以及SOA服务治理方案。
  • 简单的说,dubbox就是个服务框架,如果没有分布式的需求,其实是不需要用的
  • 只有在分布式的时候,才有dubbox这样的分布式服务框架的需求
  • 说白了就是个 远程服务调用的分布式框架。

分布式基础理论

  • 什么是分布式系统?
  • 发展演变

什么叫RPC

  • RPC【Remote Procedure Call】是指远程过程调用,是一种**进程间通信方式** 是一种技术的思想,而不是规范
  • 它允许程序调用另一个地址空间 (通常指共享网络的另一台机器上) 的过程或方法, 而不用程序员显式编码这个远程调用的细节。 即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
RPC基本原理
  • 首先调用方需要有个RPCclient,被调用方需要有 RCPServer,这两个服务用于RPC通信。
  • 调用者通过RPCClient调用指定方法 RPCClient则将请求封装后(将方法参数值进行二进制序列化),传递给server
  • server收到请求后,反序列化参数,恢复成请求原本的形式 然后找到对应的方法进行本地调用。将方法的返回值通过RPC返回给client
  • client收到结果后,将结果返回给调用者作为返回值。

Dubbox基本概念

  • 服务提供者(Provider): 暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
  • 服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 注册中心(Registry): 注册中心返回服务提供者地址列表给消费者 如果有变更,注册中心将基于长连接推送变更数据给消费者
  • 监控中心(Monitor): 服务消费者和提供者,在内存中累计调用次数和调用时间 定时每分钟发送一次统计数据到监控中心

环境搭建:

工具环境:

  • windows 操作系统
  • zookeeper消费、提供服务。 注册中心
  • 官方提供了一个可视化的监控程序 dubbo-admin Github 下载地址 这个可以不用必须安装只是一个管控界面… Git上下载源码之后,通过Maven 打个war包,部署本地Tomcat 或其它容器直接运行即可!

Zookeeper

  • 这里Zookeeper 只是提供一个注册中心,**并不需要细致讲解..** 安装启动即可!
Zookeeper 配置:
  • dataDir 日志文件地址
  • clientPort 服务端口
注意:

Zookeeper 解压启动报错:

  • 这是因为缺少 zoo.cof配置文件
  • 因为,Linux上是 zoo_sample.cfg 直选Copy一份改名 zoo.cfg 则可以正常启动!

dubbo-admin

  • CMD 项目目录下: mvn install -Dmaven.test.skip=true Maven打包
  • Maven学习地址
配置dubbo-admin-2.8.4 链接 zookeepk 管控信息
  • dubbo默认的注册机制是zookeeper,地址也是本地地址:127.0.0.1:2181 假如你此时zookeeper的实例的地址不是127.0.0.1:2181,或者注册机制是Redis的话,需要修改dubbo.properties的配置
  • 默认用户:root   密码:guest
随后启动tomcat
  • 打完的包,放在一个指向容器中 Tomcat 的webapps
  • 随后启动tomcat bin目录下 ./startup.sh
控制台
  • 访问 http://localhost:8080/dubbo-admin-2.8.4/访问后台
  • 8080是我的Tomcat默认端口,自行更改!
  • 账户:root 密码:root

SpringBoot整合 Double

  • 使用SpringBoot与Dubbo整合,测试远程调用 演示用户服务(consumer)查找订单(provider)的操作
  • 项目案例下载地址!

父工程依赖:

代码语言:javascript复制
    <!-- 父工程依赖! -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

common 公共实体类

两个实体类就不解释了…

Order.Java

代码语言:javascript复制
import java.io.Serializable;
public class Order implements Serializable {        //因为要进行网络传输,对实体类进行序列化操作

    private Integer id;
    private String title;
    private Integer price;
    private Integer uid;
    //省略 get/set..
}

User.Java

代码语言:javascript复制
public class User {
    private Integer id;
    private String nickname;
    private List<Order> orders;
    //省略 get/set。。。
}
rpcServier RPC业务接口包:
  • 主要因为:Order提供方,User调用方调用代码需要调用api方法,而服务跨模块了并调用不到…
  • 将接口,声明在公共的模块中,user调用方 和order实现方就都可以获取到调用或实现方法!

RPCOrderService.Java

代码语言:javascript复制
import com.wsm.entity.Order;
import java.util.List;
public interface RPCOrderService {
    public List<Order> findUserOrder(Integer uid);
}

Order 订单模块

实现 RPCService 接口:

RPCOrderServiceImpl.Java

代码语言:javascript复制
import com.alibaba.dubbo.config.annotation.Service;
import com.wsm.entity.Order;
import com.wsm.rpcServier.*;
import java.util.ArrayList;
import java.util.List;
//注意这里是 dubbo 的服务注解
@Service
public class RPCOrderServiceImpl implements RPCOrderService {
    //造假数据!
    private static List<Order> list = new ArrayList<>();
    static {
        list.add(new Order(1, "张三的订单1.0", 10, 1));
        list.add(new Order(2, "张三的订单1.0", 100, 1));
        list.add(new Order(3, "李四的订单1.0", 10, 2));
        list.add(new Order(4, "张三的订单1.0", 105, 1));
        list.add(new Order(5, "李四的订单1.0", 10, 2));
    }
    //根据输入用户id 获取对应的订单数据!
    @Override
    public List<Order> findUserOrder(Integer uid) {
        List<Order> orders = new ArrayList<>();
        for (Order order : list) {
            if (uid == order.getUid()) {
                orders.add(order);
            }
        }
        return orders;
    }
}
  • 注意 这里是的 @service 是dubbo 的服务注解 import com.alibaba.dubbo.config.annotation.Service;
  • 也就是通过该注解,配置进行扫描时候进行加载配置…
pom.xml依赖:

pom.xml

代码语言:javascript复制
    <dependencies>
        <!-- dubbo-starter依赖 -->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>0.2.0</version>
        </dependency>
        <!-- web依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <artifactId>mydubbo.common</artifactId>
            <groupId>org.example</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
yml配置:

application.yml

代码语言:javascript复制
#dubbo 配置
dubbo:
  #服务名
  application:
    name: order-server
  #dubbo注册的端口..
  protocol:
    name: dubbo
    port: 20881
  #注册中心的配置
  registry:
    protocol: zookeeper
    address: 127.0.0.1:2181
  #扫描包下注解!
  scan:
    base-packages: com.wsm.service
#配置Boot程序服务端口
server:
  port: 8001
主程序启动:

MyOrderApp.Java

  • 注意:@EnableDubbo 启动Doubbo注解
代码语言:javascript复制
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo            //启动Doubbo
public class MyOrderApp {
    public static void main(String[] args) {
        SpringApplication.run(com.wsm.MyOrderApp.class,args);
    }
}

服务注册成功!

User 用户模块

Controller 控制层请求:
  • 因为是调用方这个还是得要滴…

UserController.Java

代码语言:javascript复制
import com.wsm.entity.User;
import com.wsm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/info/{id}")
    public User info(@PathVariable Integer id){
        return userService.findUserByorder(id);
    }
}
Service RPC调用远程Order模块

接口 UserService.Java

接口实现 UserServiceImpl.Java

代码语言:javascript复制
import com.alibaba.dubbo.config.annotation.Reference;
import com.wsm.entity.Order;
import com.wsm.entity.User;
import com.wsm.rpcServier.RPCOrderService;
import com.wsm.service.UserService;
import org.springframework.stereotype.Service;
import java.util.List;

//正常的Spring的 @Service注解!
@Service
public class UserServiceImpl implements UserService {

    @Reference
    private RPCOrderService rpcOrderService;

    @Override
    public User findUserByorder(Integer id) {
        User user = new User();
        user.setId(id);
        user.setNickname("张三");
        List<Order> userOrder = rpcOrderService.findUserOrder(id);
        user.setOrders(userOrder);
        return user;
    }
}
  • 这里因为是 正常的调用方 直接Spring的 @service 即可
  • 内部调用了 rpcOrderService 的实现类… 并通过 @Reference 注解注入 分布式的远程服务的对象 需要dubbo配置使用。
  • 当你调用方服务启动时候,根据 yml配置 扫描指定包下, 即可得知当前服务需要 调用的服务模块....
pom依赖:
  • 与Order 模块一致即可!
yml配置

application.yml

代码语言:javascript复制
dubbo:
  application:
    name: user-server
  registry:
    protocol: zookeeper
    address: 127.0.0.1:2181
  scan:
    base-packages: com.wsm.service
server:
  port: 8082
主程序运行 并调用!

MyUserApp.Java

代码语言:javascript复制
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class MyUserApp {
    public static void main(String[] args) {
        SpringApplication.run(com.wsm.MyUserApp.class, args);
    }
}

结果案例

Dubbo 配置Config

  • Dubbo有很配置可供使用 并支持很多种配置…

配置方式:

Dubbo提供启动时配置、XML配置、properties以及yml配置

优先级

  • 启动参数 > XML > yml > prop
  • 如果prop和yml都存在,那么yml的优先级会大于prop yml 配置覆盖之前的配置!
  • 启动参数 JVM 启动 -D 参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。
  • XML 如果在 XML 中有配置,则 dubbo.properties 中的相应配置项无效。 常用于 Spring整合**Dubbo**
  • yml 目前流行的一种,SpringBoot的配置编写方式…
  • properties 相当于缺省值,只有 XML 没有配置时, dubbo.properties 的相应配置项才会生效,通常用于共享公共配置,比如应用名。

启动建材 配置:

  • 在**consumer消费者** 程序启动时如果**provider提供者**程序未启动会造成启动报错
  • 在生产环境中可能会存在provider晚于consumer启动的情况。
  • 启动检查默认开启,也可以将启动检查关闭。
局部关闭

Consumer**调用方 获取接口注解添加属性check,启动时会自动忽略提供provider 的必须启动**

代码语言:javascript复制
@Reference(check = false)       //自动检查配置 true/false
private RPCOrderService rpcOrderService;
全局关闭
  • 在配置文件中关闭,该操作会关闭 该 调用者 全部的 provider提供者 检查

yml

代码语言:javascript复制
dubbo:
  consumer:
    check: false

超时 配置:

  • 由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时)。
  • 为了避免这种问题导致客户端资源(线程)挂起耗尽,必须要设置超时间。
  • 默认超时时间为1s,如过1s未响应返回结果会进入 重试阶段
局部配置:
  • Consumer获取接口的注解添加属性timeout,以毫秒为单位,仅限当前这个provider
代码语言:javascript复制
@Reference(vtimeout = 3000)
private RPCOrderService rpcOrderService;
全局配置:
  • 当前调用者的所有,提供服务进行超时配置!
代码语言:javascript复制
dubbo:
  consumer:
    timeout: 3000

重试阶段 配置:

provider超时未应答,会进行重试发送。默认重试两次 第一次用户发起的不算 超时后程序自动重试的次数

局部配置:
  • 通过 retries=“2” 来设置重试次数(不含第一次)
代码语言:javascript复制
@Reference(retries = 5)
private RPCOrderService rpcOrderService;
全局配置:
  • 当前调用者的所有,提供者服务都会重试…
代码语言:javascript复制
dubbo:
  consumer:
    registry: 5
注意:

重试需要考虑幂等性问题

  • 幂等(设置重试次数) 查询 删除 修改 因为查不会更改数据库,删 改就算第一次成功了,后面在带着相同的参数还是只是在执行一次而已对数据库更改也只有一次!
  • 非幂等 (不能设置重试次数) 新增 会,可能存在对数据库的多次修改!

多版本 灰度发布

  • 灰度发布(又名金丝雀发布)是指在黑与白之间 能够平滑过渡的一种发布方式。
  • 即: 当某一程序政策需要更新时候, 让一部分用户继续用产品特性A,一部分用户开始用产品特性B 如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。
  • 灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。
  • 灰度期:灰度发布开始到结束期间的这一段时间,称为**灰度期**

那么dubbo支持这一功能,可以选择版本去获取,非常的方便

提供者,提供多个版本的实现.
  • 并指定版本
  • 消费者,消费时候,指定要消费调用的**服务版本!**
消费者,调用需要的版本!
代码语言:javascript复制
@Reference(version = "1.0.0")       //指定要调用的版本!
private RPCOrderService rpcOrderService;

Dubbo直连

  • 调用者可以通过 @Refernce配置url 指定,调用服务的地址直接连接
代码语言:javascript复制
@Reference(url = "127.0.0.1:20881")
private RPCOrderService rpcOrderService;

注意不是Boot的服务器地址,而是 Dubbo的网络端口

Dubbo高可用

  • 现象: zookeeper注册中心宕机,还可以消费dubbo暴露的服务。 当程序正在运行时,注册中心突然挂了,并不会立刻影响程序使用! 就是高可用**健壮性**
健壮性
  • 监控中心宕机不影响使用,只会丢失部分采样数据. 新的服务不会在注册 但 老的服务还是可以正常访问的
  • 数据库宕机后,注册中心仍能通过缓存提供服务列表查询,但不能注册新的服务
  • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台 zookeeper 集群环境下
  • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
  • 服务提供者无状态,任意一台宕掉后,不影响使用
  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

负载均衡策略

  • Dubbo 提供了**多种均衡策略**,缺省为 random 随机调用。
  • 多个服务提供者 情况下,消费者调用,Dubbox本身存在默认的 负载均衡进行 调用
Random LoadBalance 权重随机 默认
  • 默认**随机**,按权重设置随机概率 只是概率,但并不是绝对! 默认权重都是100
  • 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
Random LoadBalance 轮询
  • 轮循(RoundRobin),按公约后的权重设置轮循比率。
  • 存在 慢的提供者 累积请求的问题 比如:第二台机器很慢,但没死掉,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance 最少活跃
  • 最少活跃 相同活跃数的随机,活跃数指调用前后计数差。
  • Dubbo会查看上次的请求时间进行比对,如果发现速度较慢则转移到速度快的节点上。
ConsistentHash LoadBalance 一致性哈希
  • 一致性哈希 相同参数的请求总是发到同一提供者。
  • 当某一台提供者不工作时,原本发往该提供者的请求,
  • 基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。算法参见wikipedia
测试实现:

Copy 两个几乎一样的 提供者模块

  • 修改SpringBoot启动端口
  • 修改Dubbox 注册端口
  • 为了区分,方法调用时候输出一个 当前的信息
  • 启动!

User 模块消费调用:

总结:

Dubbo是基于客户端的负载均衡,相关配置需要在客户端配置。

  • 可以在@Service通过weight设置权重 @Service(weight = 权重数值)
  • 调用者通过 @Reference(loadbalance = “roundrobin”) 设置 轮询的规则
  • 设置了轮询,就忽略了权重!

整合 hystrix 熔断服务降级

服务降级
  • 当服务器压力剧增的情况下,根据实际业务情况及流量
  • 对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。
控制台设置 dubbo-admin管控台
  • 管控台也可以直接设置 权重…屏蔽 禁止等一些操作…
  • 可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。

屏蔽

  • mock=force:return null 表示消费方对该服务的方法调用都直接返回 null 值. 不发起远程调用。 用来屏蔽不重要服务不可用时对调用方的影响。

禁止

  • 还可以改为 mock=fail:return null 表示消费方对该服务的方法调用在失败后 再返回 null 值,不抛异常
  • 用来容忍不重要服务不稳定时对调用方的影响。

在控制台可以很方便的设置

整合 hystrix

  • Hystrix 旨在通过控制那些访问远程系统,服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
  • Hystrix具备拥有回退机制和断路器功能的线程和信号隔离 请求缓存和请求打包,以及监控和配置等功能
依赖

由于dubbo不自带hystrix 这是SpringCloud的东西,所以需要加入Cloud的依赖等

代码语言:javascript复制
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>1.4.4.RELEASE</version>
</dependency>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
要进行服务降级的方法:
  • 添加注解,并编写熔断降级方法!

UserServiceImpl.Java

代码语言:javascript复制
import com.alibaba.dubbo.config.annotation.Reference;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.wsm.entity.Order;
import com.wsm.entity.User;
import com.wsm.rpcServier.RPCOrderService;
import com.wsm.service.UserService;
import org.springframework.stereotype.Service;
import java.util.List;

//正常的Spring的 @Service注解!
@Service
public class UserServiceImpl implements UserService {
    /*局部配置:
        @Reference(check = false)           //自动检查配置 true/false
        @Reference(vtimeout = 3000)         //设置超时重试
        @Reference(retries = 5)             //重试机制,指定重试次数!
        @Reference(url = "127.0.0.1:20881") //直连
        @Reference(version = "1.0.0")       //指定要调用的版本!
        @Reference(loadbalance = "roundrobin") //设置轮询规则!
    * */
//    @Reference
    @Reference(loadbalance = "roundrobin")
    private RPCOrderService rpcOrderService;

    @Override
    @HystrixCommand(fallbackMethod = "findUserByorder_fallback")    //指定降级方法!
    public User findUserByorder(Integer id) {
        //手动抛出异常,查看降级结果!用户id 1的就抛出异常!
        if (id==1){
            throw new RuntimeException("异常了!");
        }
        User user = new User();
        user.setId(id);
        user.setNickname("张三");
        List<Order> userOrder = rpcOrderService.findUserOrder(id);
        user.setOrders(userOrder);
        return user;
    }

    //改降级方法要与,服务方法参数一模一样!
    public User findUserByorder_fallback(Integer id) {
        User user = new User();
        user.setId(id);
        user.setNickname("熔断方法");
        return user;
    }
}
主程序运行测试:
  • 主程序类添加 启动注解
  • @EnableHystrix        //熔断降级Hystrix 启动注解!

0 人点赞