name - artist
00:00

      目录

      Spring Cloud 系列之 Dubbo RPC 通信

      Dubbo 介绍

        

      /resources/articles/spring/spring-cloud/dubbo-rpc/dubbo.jpg

        

        官网:http://dubbo.apache.org/zh-cn/

        Github:https://github.com/apache/dubbo

        2018 年 2 月 15 日,阿里巴巴的服务治理框架 dubbo 通过投票,顺利成为 Apache 基金会孵化项目。

        Apache Dubbo 是一款高性能、轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用智能容错和负载均衡,以及服务自动注册和发现

        

      Dubbo 架构

        

        Dubbo 提供三个核心功能:面向接口的远程方法调用智能容错和负载均衡,以及服务自动注册和发现。Dubbo 框架广泛的在阿里巴巴内部使用,以及当当、去哪儿、网易考拉、滴滴等都在使用。

      /resources/articles/spring/spring-cloud/dubbo-rpc/architecture.png

      节点角色说明

        

      节点 角色说明
      Provider 暴露服务的服务提供方
      Consumer 调用远程服务的服务消费方
      Registry 服务注册与发现的注册中心
      Monitor 统计服务的调用次数和调用时间的监控中心
      Container 服务运行容器

        

      调用关系说明

        

      1. 服务容器负责启动,加载,运行服务提供者。
      2. 服务提供者在启动时,向注册中心注册自己提供的服务。
      3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
      4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
      5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
      6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

        

      Dubbo 快速入门

        

        我们先通过一个简单的案例让大家理解一下 Dubbo 的使用,然后基于 Spring Boot 和 Spring Cloud 环境整合 Dubbo。

        Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可。

        

      依赖

        

        JDK 1.6 以上和 Maven 3.0 以上,采用 Maven 多模块聚合工程构建 api 模块,provider 模块以及 consumer 模块。

        

      聚合工程

        

        项目结构如下图,简单介绍一下:

      • dubbo-api:服务接口
      • dubbo-provider:依赖服务接口,具体的业务实现,服务提供者
      • dubbo-coonsumer:依赖服务接口,远程调用服务,服务消费者

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614114932671.png

        

      依赖关系

        

        dubbo-parent 的 pom.xml 依赖 apache dubbo。

      1
      2
      3
      4
      5
      6
      7
      8
      
      <dependencies>
          <!-- apache dubbo 依赖 -->
          <dependency>
              <groupId>org.apache.dubbo</groupId>
              <artifactId>dubbo</artifactId>
              <version>2.7.4.1</version>
          </dependency>
      </dependencies>
      

        

        dubbo-provider 和 dubbo-consumer 的 pom.xml 依赖 dubbo-api 服务接口。

      1
      2
      3
      4
      5
      6
      7
      8
      
      <dependencies>
          <!-- dubbo-api 依赖 -->
          <dependency>
              <groupId>org.example</groupId>
              <artifactId>dubbo-api</artifactId>
              <version>1.0-SNAPSHOT</version>
          </dependency>
      </dependencies>
      

        

      定义服务接口

        

        dubbo-api 中编写 HelloService.java

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      package org.example.service;
      
      /**
       * Hello服务
       */
      public interface HelloService {
      
          String sayHello(String name);
      
      }
      

        

      定义服务提供者

        

      在 provider 模块中实现服务接口

        

        dubbo-provider 中编写 HelloServiceImpl.java

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      
      package org.example.service.impl;
      
      import org.example.service.HelloService;
      
      /**
       * 服务实现
       */
      public class HelloServiceImpl implements HelloService {
      
          public String sayHello(String name) {
              return "hello " + name;
          }
      
      }
      

        

      配置服务提供者

        

        dubbo-provider.xml

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
             http://dubbo.apache.org/schema/dubbo
             http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
      
          <!-- 提供方应用信息,用于计算依赖关系 -->
          <dubbo:application name="hello-world-app"/>
      
          <!-- 使用 multicast 广播注册中心暴露服务地址 -->
          <dubbo:registry address="multicast://224.5.6.7:1234"/>
      
          <!-- 用 dubbo 协议在 20880 端口暴露服务 -->
          <dubbo:protocol name="dubbo" port="20880"/>
      
          <!-- 声明需要暴露的服务接口 -->
          <dubbo:service interface="org.example.service.HelloService" ref="helloService"/>
      
          <!-- 和本地 bean 一样实现服务 -->
          <bean id="helloService" class="org.example.service.impl.HelloServiceImpl"/>
      
      </beans>
      

        

      加载 Spring 配置启动服务

        

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      
      package org.example;
      
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      /**
       * 发布服务
       */
      public class Provider {
      
          public static void main(String[] args) throws Exception {
              ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:dubbo-provider.xml");
              context.start();
              System.out.println("服务注册成功!");
              System.in.read(); // 按任意键退出
          }
      
      }
      

        

      定义服务消费者

        

      通过 Spring 配置引用远程服务

        

        dubbo-consumer.xml

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
             http://dubbo.apache.org/schema/dubbo
             http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
      
          <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
          <dubbo:application name="consumer-of-helloworld-app"  />
      
          <!-- 使用 multicast 广播注册中心暴露发现服务地址 -->
          <dubbo:registry address="multicast://224.5.6.7:1234" />
      
          <!-- 生成远程服务代理,可以和本地 bean 一样使用 helloService -->
          <dubbo:reference id="helloService" interface="org.example.service.HelloService" />
      
      </beans>
      

        

      加载 Spring 配置并调用远程服务

        

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      
      package org.example;
      
      import org.example.service.HelloService;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      /**
       * 调用远程服务
       */
      public class Consumer {
          public static void main(String[] args) throws Exception {
              ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:dubbo-consumer.xml");
              context.start();
              HelloService helloService = (HelloService) context.getBean("helloService"); // 获取远程服务代理
              String result = helloService.sayHello("world"); // 执行远程方法
              System.out.println(result); // 显示调用结果
          }
      }
      

        

      Dubbo 常用标签

        

      • dubbo:application:应用程序名称
      • dubbo:registry:连接注册中心信息(配置注册中心)
      • dubbo:protocol:服务提供者注册服务采用的协议
        • Dubbo 协议,默认
        • RMI 协议
        • Hessian 协议
        • HTTP 协议
        • WebService 协议
        • Thrift 协议
        • Memcached 协议
        • Redis 协议
        • Rest 协议(RESTful)
        • Grpc 协议
        • 更多协议信息请参考:http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html
      • dubbo:service:声明需要暴露的服务接口
      • dubbo:reference:配置订阅的服务(生成远程服务代理)

        更多配置信息请参考:http://dubbo.apache.org/zh-cn/docs/user/references/xml/introduction.html

        

      注册中心支持

        

        注册中心我们已经学习了不少,例如:ZooKeeper、Eureka、Consul、Nacos 等等,注册中心可以更高效的管理系统的服务:比如服务接口的发布、自动剔除无效的服务、自动恢复服务等。

        Dubbo 支持五种注册中心:Multicast、Nacos(推荐)、ZooKeeper(推荐) 、Redis、Simple。本文重点介绍前两个,更多注册中心的信息请参考:http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html

        

      Multicast 注册中心

        

        Multicast 注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。

      /resources/articles/spring/spring-cloud/dubbo-rpc/multicast.jpg

      1. 提供方启动时广播自己的地址
      2. 消费方启动时广播订阅请求
      3. 提供方收到订阅请求时,单播自己的地址给订阅者,如果设置了 unicast=false,则广播给订阅者
      4. 消费方收到提供方地址时,连接该地址进行 RPC 调用。

        组播受网络结构限制,只适合小规模应用或开发阶段使用。组播地址段: 224.0.0.0 - 239.255.255.255

        

      配置

        

      1
      
      <dubbo:registry address="multicast://224.5.6.7:1234" />
      

        或

      1
      
      <dubbo:registry protocol="multicast" address="224.5.6.7:1234" />
      

        或

      1
      
      dubbo.register.address=multicast://224.5.6.7:1234
      

        

        为了减少广播量,Dubbo 缺省使用单播发送提供者地址信息给消费者,如果一个机器上同时启了多个消费者进程,消费者需声明 unicast=false,否则只会有一个消费者能收到消息。

        当服务者和消费者运行在同一台机器上,消费者同样需要声明unicast=false,否则消费者无法收到消息,导致 No provider available for the service 异常。

      1
      
      <dubbo:registry address="multicast://224.5.6.7:1234?unicast=false" />
      

        或

      1
      2
      3
      
      <dubbo:registry protocol="multicast" address="224.5.6.7:1234">
          <dubbo:parameter key="unicast" value="false" />
      </dubbo:registry>
      

        

      ZooKeeper 注册中心

        

        Apache ZooKeeper 是一个开放源码的分布式应用程序协调组件,是 Hadoop 和 Hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,推荐使用。

        在微服务项目开发中 ZooKeeper 主要的角色是当做服务注册中心存在,我们将编写好的服务注册至 ZooKeeper 即可。

      /resources/articles/spring/spring-cloud/dubbo-rpc/zookeeper.jpg

        

        流程说明:

      • 服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址。
      • 服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址。
      • 监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。

        

        支持以下功能:

      • 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息;
      • 当注册中心重启时,能自动恢复注册数据,以及订阅请求;
      • 当会话过期时,能自动恢复注册数据,以及订阅请求;
      • 当设置 <dubbo:registry check="false" /> 时,记录失败注册和订阅请求,后台定时重试;
      • 可通过 <dubbo:registry username="admin" password="1234" /> 设置 zookeeper 登录信息;
      • 可通过 <dubbo:registry group="dubbo" /> 设置 zookeeper 的根节点,不配置将使用默认的根节点;
      • 支持 * 号通配符 <dubbo:reference group="*" version="*" />,可订阅服务的所有分组和所有版本的提供者。

      作为 Dubbo 的老牌黄金搭档 ZooKeeper,我们在单独讲解 Dubbo 时已经给大家分享过如何使用了,本文系 Spring Cloud Alibaba 系列文章,重点对象是 Nacos,所以 ZooKeeper 这里就不过多赘述了。

        

      Nacos 注册中心

        

        Nacos 是 Alibaba 公司推出的开源工具,用于实现分布式系统的服务发现与配置管理。Nacos 是 Dubbo 生态系统中重要的注册中心实现,其中 dubbo-registry-nacos 则是 Dubbo 融合 Nacos 注册中心的实现,推荐使用。

        Nacos 官网:https://nacos.io/zh-cn/

        Github:https://github.com/alibaba/nacos

        

      预备工作

        

        当您将 Nacos 整合到您的 Dubbo 工程之前,请确保后台已经启动 Nacos 服务。关于 Nacos 的安装和其他详细内容可参考我之前的文章 Spring Cloud 系列之 Alibaba Nacos 注册中心

        

      快速上手

        

        Dubbo 融合 Nacos 成为注册中心的操作步骤非常简单,大致步骤可分为“增加 Maven 依赖”和“配置注册中心“。

        

      依赖

        

        核心依赖主要是 dubbo-registry-nacosnacos-client

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      
      <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-registry-nacos -->
      <dependency>
          <groupId>org.apache.dubbo</groupId>
          <artifactId>dubbo-registry-nacos</artifactId>
          <version>2.7.4.1</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client -->
      <dependency>
          <groupId>com.alibaba.nacos</groupId>
          <artifactId>nacos-client</artifactId>
          <version>1.3.0</version>
      </dependency>
      

        

      配置注册中心

        

        服务提供者和服务消费者只需要调整 address 属性配置即可。

        单机配置:

      1
      2
      3
      4
      
      <!-- 使用 Nacos 注册中心,单机版 -->
      <dubbo:registry address="nacos://127.0.0.1:8848"/>
      <!-- 或 -->
      <dubbo:registry protocol="nacos" address="127.0.0.1:2181"/>
      

        或:

      1
      
      dubbo.register.address=nacos://192.168.10.101:8848
      

        或:

      1
      2
      
      dubbo.register.protocol=nacos
      dubbo.register.address=192.168.10.101:8848
      

        

        集群配置:

      1
      2
      3
      4
      
      <!-- 使用 Nacos 注册中心,集群版 -->
      <dubbo:registry address="nacos://192.168.10.101:2181?backup=192.168.10.102:2181,192.168.10.103:2181"/>
      <!-- 或 -->
      <dubbo:registry protocol="nacos" address="192.168.10.101:2181,192.168.10.102:2181,192.168.10.103:2181"/>
      

        或:

      1
      
      dubbo.register.address=nacos://192.168.10.101:8848
      

        或:

      1
      2
      
      dubbo.register.protocol=nacos
      dubbo.register.address=192.168.10.101:8848,192.168.10.102:8848,192.168.10.103:8848
      

        随后,重启您的 Dubbo 应用,Dubbo 的服务提供和消费信息在 Nacos 控制台中即可显示。

        

      Spring Cloud Alibaba Nacos 整合 Dubbo

        

        之前的文章中,无论我们学习 Eureka、Consul 还是 Nacos,负责服务间通信的功能都是由 Ribbon 来完成的,接下来我们使用 Dubbo 来替换 Ribbon。

        

      聚合工程

        

        dubbo-demo 聚合工程。SpringBoot 2.3.0.RELEASESpring Cloud Hoxton.SR5

        项目结构如下图,简单介绍一下:

      • service-api:服务接口
      • product-service:商品服务,服务提供者,提供了 /product/list 接口
      • order-service:订单服务,服务消费者,远程调用商品服务

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614165918030.png

        

      依赖关系

        

        dubbo-demo 的 pom.xml。

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
      
          <!-- 项目坐标地址 -->
          <groupId>com.example</groupId>
          <!-- 项目模块名称 -->
          <artifactId>dubbo-demo</artifactId>
          <packaging>pom</packaging>
          <!-- 项目版本名称 快照版本SNAPSHOT、正式版本RELEASE -->
          <version>1.0-SNAPSHOT</version>
          <modules>
              <module>service-api</module>
              <module>product-service</module>
              <module>order-service</module>
          </modules>
      
          <!-- 继承 spring-boot-starter-parent 依赖 -->
          <!-- 使用继承方式,实现复用,符合继承的都可以被使用 -->
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.3.0.RELEASE</version>
          </parent>
      
          <!--
              集中定义依赖组件版本号,但不引入,
              在子工程中用到声明的依赖时,可以不加依赖的版本号,
              这样可以统一管理工程中用到的依赖版本
           -->
          <properties>
              <!-- Spring Cloud Hoxton.SR5 依赖 -->
              <spring-cloud.version>Hoxton.SR5</spring-cloud.version>
              <!-- spring cloud alibaba 依赖 -->
              <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
          </properties>
      
          <!-- 项目依赖管理 父项目只是声明依赖,子项目需要写明需要的依赖(可以省略版本信息) -->
          <dependencyManagement>
              <dependencies>
                  <!-- spring cloud 依赖 -->
                  <dependency>
                      <groupId>org.springframework.cloud</groupId>
                      <artifactId>spring-cloud-dependencies</artifactId>
                      <version>${spring-cloud.version}</version>
                      <type>pom</type>
                      <scope>import</scope>
                  </dependency>
      
                  <!-- spring cloud alibaba 依赖 -->
                  <dependency>
                      <groupId>com.alibaba.cloud</groupId>
                      <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                      <version>${spring-cloud-alibaba.version}</version>
                      <type>pom</type>
                      <scope>import</scope>
                  </dependency>
              </dependencies>
          </dependencyManagement>
      
      </project>
      

        

        service-api 的 pom.xml

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <parent>
              <artifactId>dubbo-demo</artifactId>
              <groupId>com.example</groupId>
              <version>1.0-SNAPSHOT</version>
          </parent>
          <modelVersion>4.0.0</modelVersion>
      
          <artifactId>service-api</artifactId>
      
          <dependencies>
              <!-- lombok 依赖 -->
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
              </dependency>
          </dependencies>
      
      </project>
      

        

        product-service 需要依赖 Nacos 和 Dubbo 的依赖,还有 service-api 的依赖,完整依赖如下:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      
          <!-- 继承父依赖 -->
          <parent>
              <artifactId>dubbo-demo</artifactId>
              <groupId>com.example</groupId>
              <version>1.0-SNAPSHOT</version>
          </parent>
          <modelVersion>4.0.0</modelVersion>
      
          <artifactId>product-service</artifactId>
      
          <!-- 项目依赖 -->
          <dependencies>
              <!-- spring cloud alibaba nacos discovery 依赖 -->
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
              </dependency>
              <!-- spring cloud alibaba dubbo 依赖 -->
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-alibaba-dubbo</artifactId>
              </dependency>
              <!-- spring boot web 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
              <!-- service-api 依赖 -->
              <dependency>
                  <groupId>com.example</groupId>
                  <artifactId>service-api</artifactId>
                  <version>1.0-SNAPSHOT</version>
              </dependency>
      
              <!-- spring boot test 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
                  <exclusions>
                      <exclusion>
                          <groupId>org.junit.vintage</groupId>
                          <artifactId>junit-vintage-engine</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
          </dependencies>
      
      </project>
      

        

        order-service 需要依赖 Nacos 和 Dubbo 的依赖,还有 service-api 的依赖,完整依赖如下:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      
          <!-- 继承父依赖 -->
          <parent>
              <artifactId>dubbo-demo</artifactId>
              <groupId>com.example</groupId>
              <version>1.0-SNAPSHOT</version>
          </parent>
          <modelVersion>4.0.0</modelVersion>
      
          <artifactId>order-service</artifactId>
      
          <!-- 项目依赖 -->
          <dependencies>
              <!-- spring cloud alibaba nacos discovery 依赖 -->
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
              </dependency>
              <!-- spring cloud alibaba dubbo 依赖 -->
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-alibaba-dubbo</artifactId>
              </dependency>
              <!-- spring boot web 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
              <!-- service-api 依赖 -->
              <dependency>
                  <groupId>com.example</groupId>
                  <artifactId>service-api</artifactId>
                  <version>1.0-SNAPSHOT</version>
              </dependency>
      
              <!-- spring boot test 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
                  <exclusions>
                      <exclusion>
                          <groupId>org.junit.vintage</groupId>
                          <artifactId>junit-vintage-engine</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
          </dependencies>
      
      </project>
      

        

      定义服务接口

        

        我们在 service-api 模块中定义实体类和服务接口信息。

        

      实体类

        

        Product.java

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      package com.example.product.pojo;
      
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.io.Serializable;
      
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class Product implements Serializable {
      
          private Integer id;
          private String productName;
          private Integer productNum;
          private Double productPrice;
      
      }
      

        Order.java

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      
      package com.example.product.pojo;
      
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.io.Serializable;
      import java.util.List;
      
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class Order implements Serializable {
      
          private Integer id;
          private String orderNo;
          private String orderAddress;
          private Double totalPrice;
          private List<Product> productList;
      
      }
      

        

      服务接口

        

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      package com.example.product.service;
      
      import com.example.product.pojo.Product;
      
      import java.util.List;
      
      /**
       * 商品服务
       */
      public interface ProductService {
      
          /**
           * 查询商品列表
           *
           * @return
           */
          List<Product> selectProductList();
      
      }
      

        

      定义服务提供者

        

      配置文件

        

        配置文件需要配置 Nacos 注册中心和 Dubbo 相关信息,核心配置如下:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      
      server:
        port: 7070 # 端口
      
      spring:
        application:
          name: product-service # 应用名称
        # 配置 Nacos 注册中心
        cloud:
          nacos:
            discovery:
              enabled: true # 如果不想使用 Nacos 进行服务注册和发现,设置为 false 即可
              server-addr: 127.0.0.1:8848 # Nacos 服务器地址,单机版
      
      # Dubbo
      dubbo:
        # 提供方应用信息,用于计算依赖关系
        application:
          name: product-service
        # 使用 nacos 注册中心暴露服务地址
        registry:
          protocol: nacos
          address: spring-cloud://localhost
        # 用 dubbo 协议在 20880 端口暴露服务
        protocol:
          name: dubbo
          port: 20880
        # 扫描需要暴露的服务,可以被 @EnableDubbo 注解替代
        #scan:
        #  base-packages: com.example.service
      

        

      服务提供者

        

        product-service 的 ProductServiceImpl.java

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      
      package com.example.service.impl;
      
      import com.example.product.pojo.Product;
      import com.example.product.service.ProductService;
      import lombok.extern.slf4j.Slf4j;
      import org.apache.dubbo.config.annotation.Service;
      
      import java.util.Arrays;
      import java.util.List;
      
      /**
       * 商品服务
       * 		timeout 调用该服务的超时时间
       * 		version 为版本号
       * 		group 为分组
       * interface、group、version 三者确定一个服务
       */
      @Slf4j
      @Service(timeout = 5000, version = "1.0", group = "product-service")
      public class ProductServiceImpl implements ProductService {
      
          /**
           * 查询商品列表
           *
           * @return
           */
          @Override
          public List<Product> selectProductList() {
              log.info("商品服务查询商品信息...");
              return Arrays.asList(
                      new Product(1, "华为手机", 1, 5800D),
                      new Product(2, "联想笔记本", 1, 6888D),
                      new Product(3, "小米平板", 5, 2020D)
              );
          }
      
      }
      

        值得注意的是 @Service 注解不是 Spring 的注解而是 Dubbo 的注释:

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614161858982.png

        

      启动类

        

        启动类通过 @EnableDubbo 注解扫描需要暴露的服务,如果配置文件中配置了该选项,那么这里可以省略。

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      
      package com.example;
      
      import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      
      // 扫描需要暴露的服务
      @EnableDubbo(scanBasePackages = "com.example.service")
      // 开启 @EnableDiscoveryClient 注解,当前版本默认会开启该注解
      //@EnableDiscoveryClient
      @SpringBootApplication
      public class ProductServiceApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(ProductServiceApplication.class, args);
          }
      
      }
      

        

      定义服务消费者

        

      配置文件

        

        配置文件需要配置 Nacos 注册中心和 Dubbo 相关信息,核心配置如下:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      
      server:
        port: 9090 # 端口
      
      spring:
        application:
          name: order-service # 应用名称
        # 配置 Nacos 注册中心
        cloud:
          nacos:
            discovery:
              enabled: true # 如果不想使用 Nacos 进行服务注册和发现,设置为 false 即可
              server-addr: 127.0.0.1:8848 # Nacos 服务器地址,单机版
      
      # Dubbo
      dubbo:
        # 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样
        application:
          name: order-service
        # 发现 nacos 注册中心暴露的服务
        registry:
          protocol: nacos
          address: spring-cloud://localhost
        cloud:
          subscribed-services: product-service # 订阅服务,远程调用的服务名称
      

        

      服务消费者

        

        order-service 的 OrderServiceImpl.java

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      
      package com.example.service.impl;
      
      import com.example.product.pojo.Order;
      import com.example.product.service.ProductService;
      import com.example.service.OrderService;
      import lombok.extern.slf4j.Slf4j;
      import org.apache.dubbo.config.annotation.Reference;
      import org.springframework.stereotype.Service;
      
      @Slf4j
      @Service
      public class OrderServiceImpl implements OrderService {
      
          // dubbo 提供了 @Reference 注解,可替换 @Autowired 注解,用于引入远程服务
          // 如果注册服务时设置了版本及分组信息,调用远程服务时也要设置对应的版本及分组信息
          @Reference(timeout = 5000, version = "1.0", group = "product-service")
          private ProductService productService;
      
          /**
           * 根据主键查询订单
           *
           * @param id
           * @return
           */
          @Override
          public Order selectOrderById(Integer id) {
              log.info("订单服务查询订单信息...");
              return new Order(id, "order-001", "中国", 22788D,
                      productService.selectProductList());
          }
      
      }
      

        

      启动类

        

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      
      package com.example;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      
      // 开启 @EnableDiscoveryClient 注解,当前版本默认会开启该注解
      //@EnableDiscoveryClient
      @SpringBootApplication
      public class OrderServiceApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(OrderServiceApplication.class, args);
          }
      
      }
      

        

      测试

        

        先启动 Nacos 服务器,然后启动服务提供者 product-service,访问:http://localhost:8848/nacos/ 控制台显示如下:

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614183046977.png

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614183134818.png

        

        然后启动服务消费者,控制台显示如下:

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614184458183.png

        

        订单服务调用远程商品服务,结果如下:

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614183314712.png

        

      Dubbo 负载均衡

        

        在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用,也可以自行扩展负载均衡策略。

        

      负载均衡策略

        

      Random LoadBalance

        

      • 随机,按权重设置随机概率。
      • 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614194234267.png

      RoundRobin LoadBalance

        

      • 轮询,按公约后的权重设置轮询比率。
      • 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

        

      LeastActive LoadBalance

        

      • 最少活跃调用数,ping 值(延迟低)的调用,相同延迟的情况下随机。
      • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614194721917.png

      ConsistentHash LoadBalance

        

      • 一致性 Hash,相同参数的请求总是发到同一提供者。
      • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
      • 算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
      • 缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key="hash.arguments" value="0,1" />
      • 缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key="hash.nodes" value="320" />

        

      配置

        

        一般在项目中不会在代码层面指定权重,而是通过监控中心(dubbo-admin)对服务动态的指定权重,官方文档:http://dubbo.apache.org/zh-cn/docs/admin/introduction.html

        

      xml

        

        服务端服务级别

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

        客户端服务级别

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

        服务端方法级别

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

        客户端方法级别

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

        

      yaml

        

      1
      2
      3
      4
      5
      6
      
      dubbo:
        provider:
          loadbalance: roundrobin
          weight: 100
        consumer:
          loadbalance: roundrobin
      

        

      注解

        

      1
      2
      
      @Service(loadbalance = "roundrobin", weight = 100)
      @Reference(loadbalance = "roundrobin")
      

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614191151392.png

      /resources/articles/spring/spring-cloud/dubbo-rpc/image-20200614191317747.png

        至此 Dubbo RPC 通信所有的知识点就讲解结束了。

      /resources/articles/articles_bottom/end02.gif

      本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议

      大家可以通过 分类 查看更多关于 Spring Cloud 的文章。

        

      🤗 您的点赞转发是对我最大的支持。

      📖 如果我的文章为您提高了一定的工作效率,可以赞赏一下我,让我有动力写出更多好文章。

      📢 扫码关注 哈喽沃德先生「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~

      /resources/mrhelloworld/qrcode/OfficialAccounts500-500.gif

      「 感谢支持 」
       评论