Spring Cloud 系列之 Dubbo RPC 通信

Dubbo 介绍

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

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

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

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

Dubbo 架构

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

节点角色说明

节点 角色说明
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:依赖服务接口,远程调用服务,服务消费者

依赖关系

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

<dependencies>
    <!-- apache dubbo 依赖 -->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo</artifactId>
        <version>2.7.7</version>
    </dependency>
</dependencies>

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

<dependencies>
    <!-- dubbo-api 依赖 -->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>dubbo-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

定义服务接口

dubbo-api 中编写 HelloService.java

package org.example.service;

/**
 * Hello服务
 */
public interface HelloService {

    String sayHello(String name);

}

定义服务提供者

在 provider 模块中实现服务接口

dubbo-provider 中编写 HelloServiceImpl.java

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

<?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 配置启动服务

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

<?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 配置并调用远程服务

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 注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。

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

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

配置

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

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

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

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

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

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

zookeeper 注册中心

Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,推荐使用。

流程说明:

  • 服务提供者启动时: 向 /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 生态系统中重要的注册中心实现。

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

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

预备工作

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

快速上手

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

依赖

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

<!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-registry-nacos -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-nacos</artifactId>
    <version>2.7.7</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 属性配置即可。

单机配置:

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

集群配置:

<!-- 使用 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"/>

随后,重启您的 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:订单服务,服务消费者,远程调用商品服务

依赖关系

dubbo-demo 的 pom.xml。

<?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

<?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 的依赖,完整依赖如下:

<?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 的依赖,完整依赖如下:

<?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

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

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;

}

服务接口

package com.example.product.service;

import com.example.product.pojo.Product;

import java.util.List;

/**
 * 商品服务
 */
public interface ProductService {

    /**
     * 查询商品列表
     *
     * @return
     */
    List<Product> selectProductList();

}

定义服务提供者

配置文件

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

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

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 的注释:

启动类

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

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 相关信息,核心配置如下:

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

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());
    }

}

启动类

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/ 控制台显示如下:

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

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

Dubbo 负载均衡

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

负载均衡策略

Random LoadBalance

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

RoundRobin LoadBalance

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

LeastActive LoadBalance

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

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

服务端服务级别

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

客户端服务级别

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

服务端方法级别

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

客户端方法级别

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

yaml

dubbo:
  provider:
    loadbalance: roundrobin
    weight: 100
  consumer:
    loadbalance: roundrobin

注解

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

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

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

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


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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章