Dubbo |ˈdʌbəʊ| 分布式初体验 第一章 Dubbo 基础知识


Dubbo |ˈdʌbəʊ| 分布式初体验 —— 第一章 Dubbo 基础知识


1. 分布式基础知识

1.1. 什么是分布式系统

《分布式系统原理与范型》定义:

分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统

分布式系统(Distributed system)就是建立在网络之上的软件系统。

随着互联网的发展,网站应用的规模不断扩大,常规 的垂直应用的架构已经无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个 治理系统 来确保系统架构有条不紊地演进。

1.2. 发展演变

DubboArchitectureRoadmap

  • 单一应用架构:

    ​ 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

  • 垂直应用架构:

    ​ 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的 Web 框架(MVC)是关键。

  • 分布式服务架构:

    ​ 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

  • 流动计算架构:

    ​ 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

1.3. RPC

1.3.1. 什么是 RPC

RPC(Remote Procedure Call) 是指远程过程调用,是一种进程间通信方式,它是一种技术思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或者是函数,而不用程序员显式编码这个远程调用的细节,即程序员无论是调用本地还是远程函数,本质上编写的调用代码基本相同

1.3.2. RPC 基本原理

RPCBasicPrinciples

sequenceDiagram
    participant Client
    participant Client Stub
    participant Server Stub
    participant Server
    Client ->> Client Stub: 1. 客户端调用
    Client Stub ->> Client Stub: 2. 序列化
    Client Stub ->> Server Stub: 3. 发送消息
    Server Stub ->> Server Stub: 4. 反序列化
    Server Stub ->> Server: 5. 调用本地服务
    Server ->> Server: 6. 服务处理
    Server -->> Server Stub: 7. 返回处理结果
    Server Stub ->> Server Stub: 8. 将结果序列化
    Server Stub -->> Client Stub: 9. 返回消息
    Client Stub ->> Client Stub: 10. 反序列化
    Client Stub -->> Client: 11. 返回调用结果

RPC 框架的两个核心模块:

  • 通信
  • 序列化/反序列化

目前市面上 RPC 框架有很多种,例如:Dubbo,gRPC、Thrift、HSF(High Speed Service Framework)

2. Dubbo 核心概念

2.1. Dubbo 简介

Apache Dubbo |ˈdʌbəʊ| 是一款高性能、轻量级的开源 Java RPC 服务框架,提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。

官网:http://dubbo.apache.org/

源码地址:https://github.com/apache/dubbo

2.2. Dubbo 架构:

DubboArchitecture

3. Dubbo 环境搭建

3.1. 安装 Zookeeper(使用 Docker 环境)

  1. 在 DockerHub 上面下载 Zookeeper 镜像包

    docker pull zookeeper
    
  2. 启动 Zookeeper 镜像并暴露 2181 端口

    docker run --name zookeeper-01 -p 2181:2181 --restart always -d zookeeper
    
  3. 进入 Docker 环境启用 Zookeeper 客户端

    docker exec -it zookeeper bash
    
    ./bin/zkCli.sh
    

3.2. 安装 Dubbo Admin 管理界面

  1. 源码地址:https://github.com/apache/dubbo-admin

  2. 将源码下载下来后,进入 dubbo-admin-develop/dubbo-admin-server/src/main/resources/application.properties 文件中修改 admin.registry.addressadmin.config-center 为自己注册中心的地址

  3. Maven 构建包,然后直接执行生成的 Jar 包,或者是直接在 IDE 中运行(注意⚠️:JDK 版本一定要选择 1.8,否则会运行错误!

  4. 浏览器打开 127.0.0.1:8080 如果进入此页面则表示安装成功(默认登录用户名和密码均为 root,可以在前面的 properties 配置文件中修改)

    DubboAdminPage

4. Dubbo HelloWorld

4.1 提出需求

在某个电商系统中,订单服务需要调用用户服务来获取某个用户的所有地址

我们现在需要创建两个服务模块进行测试

模块 功能
订单服务 Web 模块 创建订单等
用户服务 Service 模块 查询用户地址等

测试结果:订单服务 Web 模块在 A 服务器,用户服务模块在 B 服务器,A 可以远程调用 B 的功能。

4.2. 工程架构

根据 Dubbo《服务化最佳实践》

4.2.1. 分包

建议将服务接口,服务模型,服务异常等均放在 API 包中,因为服务模型及异常也是 API 的一部分,同时,这样做也符合分包的规则:重用发布等价原则(REP),共同重用原则(CRP)。

如果需要,也可以考虑在 API 包中放置一份 Spring 的引用配置,这样使用方只需要在 Spring 加载过程中引用此配置即可,配置建议放在模块的包目录下,以免冲突,如:com.yorname.mall.xxx.dubbo-reference.xml

4.2.2. 粒度

服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo 暂未提供分布式事务支持。

服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸。

不建议使用过于抽象的通用接口,如:Map query(Map),这样的接口没有明确语义,会给后期维护带来不便。

4.2.3. 版本

每个接口都应定义版本号,为后续不兼容升级提供可能,如: <dubbo:service interface="com.xxx.XxxService" version="1.0" />

建议使用两位版本号,因为第三位版本号通常表示兼容升级,只有不兼容时才需要变更服务版本。

当不兼容时,先升级一半提供者为新版本,再将消费者全部升为新版本,然后将剩下的一半提供者升为新版本。

4.2.4. 兼容性

服务接口增加方法,或服务模型增加字段,可向后兼容,删除方法或删除字段,将不兼容,枚举类型新增字段也不兼容,需通过变更版本号升级。

各协议的兼容性不同,参见:服务协议

4.3. 创建模块

mall-interface用于存放提供者和消费者的所有的服务接口,JavaBean 实体和异常

user-service-provider用于存放服务提供者的所有服务实现

order-service-consumer用于存放服务消费者的所有服务实现

4.4 使用 Dubbo

  1. 将服务提供者注册到注册中心(暴露服务)

    1. 导入 Dubbo 依赖以及操作 Zookeeper 的客户端

      pom.xml

      <!-- Import dubbo -->
      <dependency>
          <groupId>org.apache.dubbo</groupId>
          <artifactId>dubbo</artifactId>
          <version>3.0.5</version>
      </dependency>
      <!-- The registry uses zookeeper and introduces a client that operates zookeeper -->
      <dependency>
          <groupId>org.apache.dubbo</groupId>
          <artifactId>dubbo-dependencies-zookeeper</artifactId>
          <version>3.0.5</version>
          <type>pom</type>
      </dependency>
      <dependency>
          <groupId>org.javassist</groupId>
          <artifactId>javassist</artifactId>
          <version>3.28.0-GA</version>
      </dependency>
      
    2. 配置服务提供者

      1. 指定当前服务/应用的名字(同样的服务名字相同,不要和别的服务同名)

      2. 指定注册中心的位置

      3. 指定通信规则(通信协议,通信端口)

      4. 暴露服务(ref:指向服务真正实现的对象)

        provider.xml

        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
            xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
                 
              <!-- 1. Specify the name of the current service/APP (The same service has the same name. Do not have the same name as other services)-->
            <dubbo:application name="user-service-provider"/>
            <!-- 2. Specify the location of the registry -->
            <!--<dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry>-->
            <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
            <!-- 3. Specify communication rules (communication protocol, communication port) -->
            <dubbo:protocol name="dubbo" port="20080" />
            <!-- 4. Exposure services -->
            <dubbo:service interface="com.gregperlinli.mail.service.UserService" ref="userService"/>
            <!-- Service implementation -->
            <bean id="userService" class="com.gregperlinli.mail.service.impl.UserServiceImpl" />
            <!-- Set connection timeout -->
            <dubbo:config-center timeout="10000" />
            <!-- Connect to the monitoring center -->
            <dubbo:monitor protocol="registry" />
        </beans>
        
    3. 加载 Spring 配置

      com.yourname.mall.MainApplication

        /**
         * @author gregPerlinLi
         * @since 2022-01-24
         */
        public class MainApplication &#123;
            public static void main(String[] args) throws IOException &#123;
                ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("provider.xml");
                app.start();
                System.in.read();
            &#125;
        &#125;
    
    1. 启动后,在 Dubbo Admin 中可以看到当前运行的提供者

    DubboAdminProvider

  2. 让服务消费者去注册中心订阅服务提供者的服务地址

    1. 导入 Dubbo 依赖以及操作 Zookeeper 的客户端

      pom.xml

      <!-- Import dubbo -->
      <dependency>
          <groupId>org.apache.dubbo</groupId>
          <artifactId>dubbo</artifactId>
          <version>3.0.5</version>
      </dependency>
      <!-- The registry uses zookeeper and introduces a client that operates zookeeper -->
      <dependency>
          <groupId>org.apache.dubbo</groupId>
          <artifactId>dubbo-dependencies-zookeeper</artifactId>
          <version>3.0.5</version>
          <type>pom</type>
      </dependency>
      <dependency>
          <groupId>org.javassist</groupId>
          <artifactId>javassist</artifactId>
          <version>3.28.0-GA</version>
      </dependency>
      
    2. 配置服务消费者

      consumer.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
             xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
             http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
      
            <!-- Set package scan -->
          <context:component-scan base-package="com.gregperlinli.mall.service.impl" />
          <dubbo:application name="order-service-consumer" />
          <dubbo:registry address="zookeeper://127.0.0.1:2181" />
          <!-- An excuse for declaring a remote service that needs to be invoked, generate remote service proxy -->
          <dubbo:reference interface="com.gregperlinli.mall.service.UserService" id="userService" />
          <dubbo:config-center timeout="10000" />
            <dubbo:monitor protocol="registry" />
      </beans>
      
    3. 注入远程服务

      com.yourname.mall.MainApplication

      /**
       *
       * @author gregPerlinLi
       * @since 2022-01-24
       */
      @Service
      public class OrderServiceImpl implements OrderService &#123;
          @Autowired
          UserService userService;
      
          @Override
          public void initOrder(String userId) &#123;
              // 1. Query the receiving address of the user
              List<UserAddress> addressList = userService.getUserAddressList(userId);
              System.out.println(addressList);
          &#125;
      &#125;
      
    4. 加载 Spring 配置

      com.yourname.mall.MainApplication

      /**
       * @author gregPerlinLi
       * @since 2022-01-24
       */
      public class MainApplication &#123;
          public static void main(String[] args) throws IOException &#123;
              ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("consumer.xml");
              OrderService orderService = app.getBean(OrderService.class);
              orderService.initOrder("1");
              System.in.read();
          &#125;
      &#125;
      
    5. 启动后,在 Dubbo Admin 中可以看到当前运行的消费者

      DubboAdminConsumer

5. 监控中心

5.1. Dubbo Admin

图形化的服务管理界面;安装时需要指定注册中心地址,即可从注册中心中获取到所有的提供者/消费者进行配置管理

新版:

DubboAdminPage

旧版:

DubboAdminOldPage

5.2. Dubbo Monitor Simple

一个简单的 Dubbo 控制中心(Dubbo3.0 后已停止更新)

DubboMonitorSimplePage

6. Dubbo 与 SpringBoot 结合

6.1. 将服务提供者注册到注册中心

  1. 引入依赖:

    1. 导入 dubbo-spring-boot-starter

      版本要求:

      Versions Java SpringBoot Dubbo
      0.2.0 1.8+ 2.0.x 2.6.2+
      0.1.1 1.7+ 1.5.x 2.6.2+
    2. 导入 Dubbo 的其他依赖

    pom.xml

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-dependencies-zookeeper</artifactId>
        <version>3.0.5</version>
        <type>pom</type>
    </dependency>
    
  2. 配置 Dubbo

    application.yaml

    dubbo:
      application:
        name: user-service-provider
      registry:
        protocol: zookeeper
        address: 127.0.0.1:2181
      protocol:
        name: dubbo
        port: 11451
      config-center:
        timeout: 10000
      monitor:
        protocol: registry
    
  3. 暴露服务

    com.yourname.mall.service.impl.UserServiceImpl

    /**
     * &#123;@code @DubboService&#125;: Exposure services
     * 
     * @author gregPerlinLi
     * @since 2022-01-24
     */
    @DubboService
    @Service
    public class UserServiceImpl implements UserService &#123;
        @Override
        public List<UserAddress> getUserAddressList(String userId) &#123;
            UserAddress address1 = new UserAddress(1, "Address1", "1", "XiaoMing", "1234567890", "Y");
            UserAddress address2 = new UserAddress(2, "Address2", "2", "XiaoHong", "9876543210", "Y");
            return Arrays.asList(address1, address2);
        &#125;
    &#125;
    
  4. 在主程序下启用基于注解的 Dubbo 功能

    com.yourname.mall.BootUserServiceProviderApplication

    /**
     * &#123;@code @EnableDubbo&#125;: Enable annotation based Dubbo functionality
     *
     * @author gregperlinli
     */
    @SpringBootApplication
    @EnableDubbo
    public class BootUserServiceProviderApplication &#123;
        public static void main(String[] args) &#123;
            SpringApplication.run(BootUserServiceProviderApplication.class, args);
        &#125;
    &#125;
    
  5. 启动主程序

6.2. 让服务消费者去注册中心订阅服务提供者的服务地址

  1. 引入依赖

    pom.xml

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-dependencies-zookeeper</artifactId>
        <version>3.0.5</version>
        <type>pom</type>
    </dependency>
    
  2. 配置 Dubbo

    application.yaml

    server:
      port: 8060
    dubbo:
      application:
        name: boot-order-service-consumer
      registry:
        address: zookeeper://127.0.0.1:2181
      config-center:
        timeout: 10000
      monitor:
        protocol: registry
    
  3. 声明需要调用的远程服务接口并注入

    com.yourname.mall.service.impl.OrderServiceImpl

    /**
     * 1. Register the service provider with the registry (Exposure services)
     *      1. Import Dubbo dependency and the client (cursor) that operates zookeeper
     *      2. Configure service providers
     * 2. Let the service consumer go to the registry to subscribe to the service address of the service provider
     *
     * @author gregPerlinLi
     * @since 2022-01-24
     */
    @Service
    public class OrderServiceImpl implements OrderService &#123;
        @DubboReference
        UserService userService;
    
        @Override
        public List<UserAddress> initOrder(String userId) &#123;
            System.out.println("User id: " + userId);
            // 1. Query the receiving address of the user
            List<UserAddress> addressList = userService.getUserAddressList(userId);
            for (UserAddress userAddress : addressList) &#123;
                System.out.println(userAddress.getUserAddress());
            &#125;
            return addressList;
        &#125;
    &#125;
    

    com.yourname.mall.controller.OrderController

    /**
     * @author gregPerlinLi
     * @since 2022-01-24
     */
    @Controller
    public class OrderController &#123;
        @Autowired
        OrderService orderService;
    
        @ResponseBody
        @RequestMapping(value = "/init-order")
        public List<UserAddress> initOrder(@RequestParam("uid") String userId) &#123;
            return orderService.initOrder(userId);
        &#125;
    &#125;
    
  4. 在主程序下启用基于注解的 Dubbo 功能

    com.gregperlinli.mall.BootOrderServiceConsumerApplication

    /**
     * @author gregperlinli
     */
    @SpringBootApplication
    @EnableDubbo
    public class BootOrderServiceConsumerApplication &#123;
        public static void main(String[] args) &#123;
            SpringApplication.run(BootOrderServiceConsumerApplication.class, args);
        &#125;
    &#125;
    
  5. 启动主程序



文章作者: gregPerlinLi
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 gregPerlinLi !
  目录