Dubbo

Dubbo

轻量级 Java RPC框架

RPC简单原理

rpc

Dubbo简单架构

dubbo

一、安装与启动

1. 安装注册中心

zookeeper

一个树形目录服务,支持变更推送,适合作为Dubbo的注册中心

zookeeper

2. 安装Dubbo管理控制台

进入GitHub下载压缩包

apache/dubbo-admin

使用maven打包并运行

mvn

  • 找到 pom文件所在目录

    • 打开终端输入

      1
      2
      rem 跳过测试打包
      mvn clean package -Dmaven.test.skip=true
  • 找到 jar包所在目录

    • 打开终端输入

      1
      2
      rem 启动jar包
      java -jar xxx.jar
  • ui 则使用 npm打包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # install dependencies
    npm install

    # serve with hot reload at localhost:8080
    npm run dev

    # build for production with minification
    npm run build

    # build for production and view the bundle analyzer report
    npm run build --report
  • 启动完成

    默认进入localhost:38082端口访问

    具体则在 ui 包中的 vue.config.js 文件中查找

3. 编写示例项目

使用XML

目录结构

├─customer
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ │ AppC.java
│ │ │ │ │
│ │ │ │ └─service
│ │ │ │ OrderServiceImpl.java
│ │ │ │
│ │ │ └─resources
│ │ │ consumer.xml
├─interface
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ ├─bean
│ │ │ │ │ User.java
│ │ │ │ │
│ │ │ │ └─service
│ │ │ │ OrderService.java
│ │ │ │ UserService.java
├─provider
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ │ App.java
│ │ │ │ │
│ │ │ │ └─service
│ │ │ │ UserServiceImpl.java
│ │ │ │
│ │ │ └─resources
│ │ │ provider.xml

  • 接口放在 interface 中单独管理
  • 实现类各写在 消费者 与 提供者 中
  • 消费者通过 Dubbo 远程调用提供者的接口实现
  • provider.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!--    指定当前服务的名字-->
    <dubbo:application name="user-service-provider"/>

    <!-- 指定注册中心的位置-->
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>

    <!-- 指定通信规则 (通信协议 , 通信端口)-->
    <dubbo:protocol name="dubbo" port="20880"/>

    <!-- 暴露服务 ref指向实现对象-->
    <dubbo:service interface="service.UserService" ref="userService"/>
    <!-- 服务的实现对象-->
    <bean id="userService" class="service.UserServiceImpl"/>

    <!-- 配置监控中心-->
    <dubbo:monitor protocol="registry"/>
  • consumer.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--    设置包扫描       -->
    <context:component-scan base-package="service"/>

    <dubbo:application name="order-service-consumer"/>
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

    <!-- 声明需要的接口服务-->
    <dubbo:reference id="userService" interface="service.UserService"/>
    <!-- 配置监控中心 -->
    <dubbo:monitor protocol="registry"/>
  • 两者都在 pom 文件中导入接口

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.dutest</groupId>
    <artifactId>interface</artifactId>
    <version>1.0-SNAPSHOT</version>
    </dependency>
  • 提供者向注册中心 注册(暴露)提供的服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class UserServiceImpl implements UserService{
    @Override
    public List<User> getUsers(String name) {
    User aa = new User("AA", 12);
    User bb = new User("BB", 12);

    List<User> list = new ArrayList<>();
    list.add(aa);
    list.add(bb);
    return list;
    }
    }
    1
    2
    3
    4
    5
    6
    public class App {
    public static void main(String[] args){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("provider.xml");
    ioc.start();
    }
    }
  • 消费者向注册中心订阅(调用)提供者接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Service
    public class OrderServiceImpl implements OrderService {

    @Autowired
    UserService userService;

    @Override
    public List<User> initOrder(String name) {
    return userService.getUsers("AA");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    public class AppC {
    public static void main(String[] args) {
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("consumer.xml");
    OrderService orderService = ioc.getBean(OrderService.class);
    orderService.initOrder("AA");
    }
    }

使用Springboot

Springboot则使用注解与yml配置文件,配置注册中心 与暴露和需要的服务

  • yml

    • provider
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    dubbo:
    application:
    name: user-service-provider
    registry:
    address: 127.0.0.1:2181
    protocol: zookeeper
    protocol:
    name: dubbo
    port: 20880
    monitor:
    protocol: registry
    • consumer
    1
    2
    3
    4
    5
    6
    7
    8
    9
    dubbo:
    application:
    name: order-service-consumer
    registry:
    address: zookeeper://127.0.0.1:2181
    monitor:
    protocol: registry
    server:
    port: 8081
  • 同样在pom文件中导入接口

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.dutest</groupId>
    <artifactId>interface</artifactId>
    <version>1.0-SNAPSHOT</version>
    </dependency>
  • 提供者向注册中心 注册(暴露)提供的服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Service
    // 暴露服务
    @DubboService
    public class UserServiceImpl implements UserService {
    @Override
    public List<User> getUsers(String name) {
    User aa = new User("AA", 12);
    User bb = new User("BB", 12);

    List<User> list = new ArrayList<>();
    list.add(aa);
    list.add(bb);
    return list;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @EnableDubbo // 启用Dubbo
    @SpringBootApplication
    public class ProviderssApplication {

    public static void main(String[] args) {
    SpringApplication.run(ProviderssApplication.class, args);
    }

    }
  • 消费者向注册中心订阅(调用)提供者接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Service
    public class OrderServiceImpl implements OrderService {

    // 从注册中心远程引用
    @DubboReference
    UserService userService;

    @Override
    public List<User> initOrder(String name) {
    List<User> list = userService.getUsers("AA");
    return list;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @RequestMapping("/order")
    @RestController
    public class OrderController {

    @Autowired
    OrderService orderService;

    @RequestMapping("/init")
    public List<User> initOrder(@RequestParam("name") String name){
    return orderService.initOrder(name);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @EnableDubbo
    @SpringBootApplication
    public class ConsumerApplication {

    public static void main(String[] args) {
    SpringApplication.run(ConsumerApplication.class, args);
    }

    }

二、配置

配置关系

conf

1. 启动检测

spring 启动后会根据配置文件默认检查注册中心是否存在需要的服务,没有则报错

  • 通过xml设置启动不检查

    1
    2
    <dubbo:reference id="userService" interface="service.UserService" 
    check="false"/>

    全局配置所有消费者

    1
    <dubbo:consumer check="false"/>

    配置注册中心启动不检查

    1
    <dubbo:registry check="false"/>

2. 超时与重试

防止,由于网络或其他原因,使消费者请求长时间没有回应,造成线程阻塞

  • 默认值为 1000ms

优先级

  • 精确优先
  • 消费者优先

timeout

重试

超时失败后重试次数

1
2
<dubbo:reference id="userService" interface="service.UserService" 
retries="3"/>

==注:==

  • 幂等操作设置重试次数
    • 多次操作产生的结果一致
      • 查询
      • 删除
      • 修改
  • 非幂等操作不能设置重试次数
    • 多次操作产生的结果不一致
      • 新增

3. 多版本

一个接口的实现的不同版本

  • provider

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!--  v1		-->
    <dubbo:service interface="service.UserService" ref="userService" version="1.0.0"/>
    <!-- 服务的实现对象-->
    <bean id="userService" class="service.UserServiceImpl"/>

    <!-- v2 -->
    <dubbo:service interface="service.UserService" ref="userService2" version="2.0.0"/>
    <!-- 服务的实现对象-->
    <bean id="userService2" class="service.UserServiceImpl"/>
  • consumer

    1
    2
    <!--    指定需要的版本-->
    <dubbo:reference id="userService" interface="service.UserService" version="1.0.0"/>
    • 随机版本(灰度发布)
    1
    2
    <!--    指定需要的版本-->
    <dubbo:reference id="userService" interface="service.UserService" version="*"/>

4. 本地存根

消费者在调用远程接口前

存根可以先进行一些校验

  • 在接口模块中新建存根类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class UserServiceStub implements UserService{

    // 构造器输入UserService
    private final UserService userService;

    /**
    * 传入的的是userService远程代理对象
    *
    * @param userService 用户服务
    */
    public UserServiceStub(UserService userService) {
    super();
    this.userService = userService;
    }

    @Override
    public List<User> getUsers(String name) {
    if (StringUtils.hasLength(name)) {
    return userService.getUsers(name);
    }
    return null;
    }
    }
  • consumer.xml中配置指定使用的存根

    1
    2
    <dubbo:reference id="userService" interface="service.UserService" version="1.0.0"
    stub="service.UserServiceStub"/>

三、高可用

1. Zookeeper宕机与Dubbo直连

Zookeeper宕机

dj

Dubbo直连

  • 绕过注册中心,直接连接远程服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Service
    public class OrderServiceImpl implements OrderService {

    // 从注册中心远程引用
    // @DubboReference
    // Dubbo 直连
    @DubboReference(url = "127.0.0.1:20880")
    UserService userService;

    @Override
    public List<User> initOrder(String name) {
    List<User> list = userService.getUsers("AA");
    return list;
    }
    }

2. 集群模式下的负载均衡

Random LoadBalance

rlb

RandomRobin LoadBalance

rrlb

LeastActive LoadBalance

lalb

ConsistentHash LoadBalance

chlb

服务端服务级别

1
<dubbo:service interface="..." loadbalance="roundrobin" />

客户端服务级别

1
<dubbo:reference interface="..." loadbalance="roundrobin" />

服务端方法级别

1
2
3
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:service>

客户端方法级别

1
2
3
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:reference>

3. 服务降级

当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,

从而释放服务器资源以保证核心交易正常运作或高效运作。

配置

1
mock="[fail|force]return|throw xxx"
  • fail 或 force 关键字可选,表示调用失败或不调用强制执行 mock 方法,如果不指定关键字默认为 fail
  • return 表示指定返回结果,throw 表示抛出指定异常
  • xxx 根据接口的返回类型解析,可以指定返回值或抛出自定义的异常

例:

1
2
3
4
5
6
7
8
9
10
11
12
<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="return" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="return null" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="fail:return aaa" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="force:return true" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService" mock="fail:throw" />

<dubbo:reference id="demoService" interface="com.xxx.service.DemoService"
mock="force:throw java.lang.NullPointException" />

配合 dubbo-admin 使用

  • 应用消费端引入 dubbo-mock-admin依赖
  • 应用消费端启动时设置 JVM 参数,-Denable.dubbo.admin.mock=true
  • 启动 dubbo-admin,在服务 Mock-> 规则配置菜单下设置 Mock 规则

以服务方法的维度设置规则,设置返回模拟数据,动态启用/禁用规则

注意事项

Dubbo 启动时会检查配置,当 mock 属性值配置有误时会启动失败,可根据错误提示信息进行排查

  • 配置格式错误,如 return+null 会报错,被当做 mock 类型处理,return 后面可省略不写或者跟空格后再跟返回值
  • 类型找不到错误,如自定义 mock 类、throw 自定义异常,请检查类型是否存在或是否有拼写错误

4. 集群容错

整合Hystrix

  • 导入依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupld>org.springframework.cloud</groupld>
    <artifactld>spring-cloud-starter-netflix-hystrix</artifactld>
    <version>1.4.4.RELEASE</version>
    </dependency>
  • 在启动类激活

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @EnableDubbo(scanBasePackages = "com.example.providerss") // 启用Dubbo
    @EnableHystrix // 启用容错
    @SpringBootApplication
    public class ProviderssApplication {

    public static void main(String[] args) {
    SpringApplication.run(ProviderssApplication.class, args);
    }

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @EnableDubbo
    @EnableHystrix
    @SpringBootApplication
    public class ConsumerApplication {

    public static void main(String[] args) {
    SpringApplication.run(ConsumerApplication.class, args);
    }

    }
  • 在指定方法上加入Hystrix管理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Service
    // 暴露服务
    @DubboService
    public class UserServiceImpl implements UserService {

    @HystrixCommand(fallbackMethod = "hello")
    @Override
    public List<User> getUsers(String name) {
    User aa = new User("AA", 12);
    User bb = new User("BB", 12);

    List<User> list = new ArrayList<>();
    list.add(aa);
    list.add(bb);
    return list;
    }

    /**
    * 出错时调用
    */
    public List<User> hello(String name) {
    return Arrays.asList(new User("CC", 12), new User("DD", 12));
    }
    }

四、原理

1. RPC与Netty原理

RPC

rpc

Netty

Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。

它极大地简化并简化了TCP和 UDP套接字服务器等网络编程。

  • NIO

    nio

  • netty则是基于nio框架

    netty

2. Dubbo框架

kj


Dubbo
https://kongke7.github.io/2023/12/11/Dubbo/
作者
Kongke
发布于
2023年12月11日
许可协议