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 的文章。


🤗 您的點贊轉發是對我最大的支持。

📢 掃碼關注 哈嘍沃德先生「文檔 + 視頻」每篇文章都配有專門視頻講解,學習更輕鬆噢 ~

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