DubboRPC通訊

Dubbo 介紹

DubboRPC通訊

2018 年 2 月 15 日,阿里巴巴的服務治理框架 dubbo 透過投票,順利成為 Apache 基金會孵化專案。

Apache Dubbo 是一款高效能、輕量級的開源 Java RPC 框架,它提供了三大核心能力:面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現。

Dubbo 架構

Dubbo 提供三個核心功能:基於介面的遠端呼叫、容錯和負載均衡,以及服務的自動註冊與發現。Dubbo 框架廣泛的在阿里巴巴內部使用,以及噹噹、去哪兒、網易考拉、滴滴等都在使用。

DubboRPC通訊

節點角色說明

| 節點 | 角色說明 | | ——————- | ———————————————————— | |

Provider

| 暴露服務的服務提供方 | |

Consumer

| 呼叫遠端服務的服務消費方 | |

Registry

| 服務註冊與發現的註冊中心 | |

Monitor

| 統計服務的呼叫次數和呼叫時間的監控中心 | |

Container

| 服務執行容器 |

呼叫關係說明

服務容器負責啟動,載入,執行服務提供者。

服務提供者在啟動時,向註冊中心註冊自己提供的服務。

服務消費者在啟動時,向註冊中心訂閱自己所需的服務。

註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連線推送變更資料給消費者。

服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一臺提供者進行呼叫,如果呼叫失敗,再選另一臺呼叫。

服務消費者和提供者,在記憶體中累計呼叫次數和呼叫時間,定時每分鐘傳送一次統計資料到監控中心。

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

:依賴服務介面,遠端呼叫服務,服務消費者

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-v8VaFKLD-1626229842283)(DubboRPC通訊。assets/image-20200614114932671。png)]

依賴關係

dubbo-parent 的 pom。xml 依賴 apache dubbo。

<!—— apache dubbo 依賴 ——> org。apache。dubbo dubbo 2。7。7

dubbo-provider 和 dubbo-consumer 的 pom。xml 依賴 dubbo-api 服務介面。

<!—— dubbo-api 依賴 ——> org。example dubbo-api 1。0-SNAPSHOT

定義服務介面

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”?> <!—— 提供方應用資訊,用於計算依賴關係 ——> <!—— 使用 multicast 廣播註冊中心暴露服務地址 ——> <!—— 用 dubbo 協議在 20880 埠暴露服務 ——> <!—— 宣告需要暴露的服務介面 ——> <!—— 和本地 bean 一樣實現服務 ——>

載入 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”?> <!—— 消費方應用名,用於計算依賴關係,不是匹配條件,不要與提供方一樣 ——> <!—— 使用 multicast 廣播註冊中心暴露發現服務地址 ——> <!—— 生成遠端服務代理,可以和本地 bean 一樣使用 helloService ——>

載入 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 協議

dubbo:service

:宣告需要暴露的服務介面

dubbo:reference

:配置訂閱的服務(生成遠端服務代理)

註冊中心

註冊中心我們已經學習了不少,例如:ZooKeeper、Eureka、Consul、Nacos 等等,註冊中心可以更高效的管理系統的服務:比如服務介面的釋出、自動剔除無效的服務、自動恢復服務等。

Dubbo 支援五種註冊中心:Multicast、Nacos(推薦)、ZooKeeper(推薦) 、Redis、Simple。本文重點介紹前兩個,更多註冊中心的資訊請參考:http://dubbo。apache。org/zh-cn/docs/user/references/registry/introduction。html

Multicast 註冊中心

Multicast 註冊中心不需要啟動任何中心節點,只要廣播地址一樣,就可以互相發現。

DubboRPC通訊

提供方啟動時廣播自己的地址

消費方啟動時廣播訂閱請求

提供方收到訂閱請求時,單播自己的地址給訂閱者,如果設定了

unicast=false

,則廣播給訂閱者

消費方收到提供方地址時,連線該地址進行 RPC 呼叫。

組播受網路結構限制,只適合小規模應用或開發階段使用。組播地址段: 224。0。0。0 - 239。255。255。255

配置

為了減少廣播量,Dubbo 預設使用單播發送提供者地址資訊給消費者,如果一個機器上同時啟了多個消費者程序,消費者需宣告

unicast=false

,否則只會有一個消費者能收到訊息。

當服務者和消費者執行在同一臺機器上,消費者同樣需要宣告

unicast=false

,否則消費者無法收到訊息,導致 No provider available for the service 異常。

zookeeper 註冊中心

Zookeeper 是 Apache Hadoop 的子專案,是一個樹型的目錄服務,支援變更推送,適合作為 Dubbo 服務的註冊中心,工業強度較高,可用於生產環境,推薦使用。

DubboRPC通訊

流程說明:

服務提供者啟動時: 向

/dubbo/com。foo。BarService/providers

目錄下寫入自己的 URL 地址。

服務消費者啟動時: 訂閱

/dubbo/com。foo。BarService/providers

目錄下的提供者 URL 地址。並向

/dubbo/com。foo。BarService/consumers

目錄下寫入自己的 URL 地址。

監控中心啟動時: 訂閱

/dubbo/com。foo。BarService

目錄下的所有提供者和消費者 URL 地址。

支援以下功能:

當提供者出現斷電等異常停機時,註冊中心能自動刪除提供者資訊;

當註冊中心重啟時,能自動恢復註冊資料,以及訂閱請求;

當會話過期時,能自動恢復註冊資料,以及訂閱請求;

當設定

時,記錄失敗註冊和訂閱請求,後臺定時重試;

可透過

設定 zookeeper 登入資訊;

可透過

設定 zookeeper 的根節點,不配置將使用預設的根節點;

支援

*

號萬用字元

,可訂閱服務的所有分組和所有版本的提供者。

作為 Dubbo 的老牌黃金搭檔 ZooKeeper,我們在單獨講解 Dubbo 時已經給大家分享過如何使用了,本文系 Spring Cloud Alibaba 系列文章,重點物件是 Nacos,所以 ZooKeeper 這裡就不過多贅述了。

Nacos 註冊中心

Nacos 是 Alibaba 公司推出的開源工具,用於實現分散式系統的服務發現與配置管理。Nacos 是 Dubbo 生態系統中重要的註冊中心實現。

預備工作

當您將 Nacos 整合到您的 Dubbo 工程之前,請確保後臺已經啟動 Nacos 服務。

快速上手

Dubbo 融合 Nacos 成為註冊中心的操作步驟非常簡單,大致步驟可分為“增加 Maven 依賴”和“配置註冊中心“。

依賴

核心依賴主要是

dubbo-registry-nacos

nacos-client

<!—— https://mvnrepository。com/artifact/org。apache。dubbo/dubbo-registry-nacos ——> org。apache。dubbo dubbo-registry-nacos 2。7。7<!—— https://mvnrepository。com/artifact/com。alibaba。nacos/nacos-client ——> com。alibaba。nacos nacos-client 1。3。0

配置註冊中心

服務提供者和服務消費者只需要調整

address

屬性配置即可。

單機配置:

<!—— 使用 Nacos 註冊中心,單機版 ——><!—— 或 ——>

叢集配置:

<!—— 使用 Nacos 註冊中心,叢集版 ——><!—— 或 ——>

隨後,重啟您的 Dubbo 應用,Dubbo 的服務提供和消費資訊在 Nacos 控制檯中即可顯示。

Spring Cloud Alibaba Nacos 整合 Dubbo

之前的文章中,無論我們學習 Eureka、Consul 還是 Nacos,負責服務間通訊的功能都是由 Ribbon 來完成的,接下來我們使用 Dubbo 來替換 Ribbon。

聚合工程

dubbo-demo

聚合工程。

SpringBoot 2。3。0。RELEASE

Spring Cloud Hoxton。SR5

專案結構如下圖,簡單介紹一下:

service-api

:服務介面

product-service

:商品服務,服務提供者,提供了

/product/list

介面

order-service

:訂單服務,服務消費者,遠端呼叫商品服務

DubboRPC通訊

依賴關係

dubbo-demo 的 pom。xml。

<?xml version=“1。0” encoding=“UTF-8”?> 4。0。0 <!—— 專案座標地址 ——> com。example <!—— 專案模組名稱 ——> dubbo-demo pom <!—— 專案版本名稱 快照版本SNAPSHOT、正式版本RELEASE ——> 1。0-SNAPSHOT service-api product-service order-service <!—— 繼承 spring-boot-starter-parent 依賴 ——> <!—— 使用繼承方式,實現複用,符合繼承的都可以被使用 ——> org。springframework。boot spring-boot-starter-parent 2。3。0。RELEASE <!—— 集中定義依賴元件版本號,但不引入, 在子工程中用到宣告的依賴時,可以不加依賴的版本號, 這樣可以統一管理工程中用到的依賴版本 ——> <!—— Spring Cloud Hoxton。SR5 依賴 ——> Hoxton。SR5 <!—— spring cloud alibaba 依賴 ——> 2。1。0。RELEASE <!—— 專案依賴管理 父專案只是宣告依賴,子專案需要寫明需要的依賴(可以省略版本資訊) ——> <!—— spring cloud 依賴 ——> org。springframework。cloud spring-cloud-dependencies ${spring-cloud。version} pom import <!—— spring cloud alibaba 依賴 ——> com。alibaba。cloud spring-cloud-alibaba-dependencies ${spring-cloud-alibaba。version} pom import

service-api 的 pom。xml

<?xml version=“1。0” encoding=“UTF-8”?> dubbo-demo com。example 1。0-SNAPSHOT 4。0。0 service-api <!—— lombok 依賴 ——> org。projectlombok lombok

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

<?xml version=“1。0” encoding=“UTF-8”?> <!—— 繼承父依賴 ——> dubbo-demo com。example 1。0-SNAPSHOT 4。0。0 product-service <!—— 專案依賴 ——> <!—— spring cloud alibaba nacos discovery 依賴 ——> com。alibaba。cloud spring-cloud-starter-alibaba-nacos-discovery <!—— spring cloud alibaba dubbo 依賴 ——> com。alibaba。cloud spring-cloud-alibaba-dubbo <!—— spring boot web 依賴 ——> org。springframework。boot spring-boot-starter-web <!—— service-api 依賴 ——> com。example service-api 1。0-SNAPSHOT <!—— spring boot test 依賴 ——> org。springframework。boot spring-boot-starter-test test org。junit。vintage junit-vintage-engine

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

<?xml version=“1。0” encoding=“UTF-8”?> <!—— 繼承父依賴 ——> dubbo-demo com。example 1。0-SNAPSHOT 4。0。0 order-service <!—— 專案依賴 ——> <!—— spring cloud alibaba nacos discovery 依賴 ——> com。alibaba。cloud spring-cloud-starter-alibaba-nacos-discovery <!—— spring cloud alibaba dubbo 依賴 ——> com。alibaba。cloud spring-cloud-alibaba-dubbo <!—— spring boot web 依賴 ——> org。springframework。boot spring-boot-starter-web <!—— service-api 依賴 ——> com。example service-api 1。0-SNAPSHOT <!—— spring boot test 依賴 ——> org。springframework。boot spring-boot-starter-test test org。junit。vintage junit-vintage-engine

定義服務介面

我們在

service-api

模組中定義實體類和服務介面資訊。

實體類

package com。example。product。pojo;import lombok。AllArgsConstructor;import lombok。Data;import lombok。NoArgsConstructor;import java。io。Serializable;@Data@NoArgsConstructor@AllArgsConstructorpublic class Product implements Serializable { private Integer id; private String productName; private Integer productNum; private Double productPrice;}package com。example。product。pojo;import lombok。AllArgsConstructor;import lombok。Data;import lombok。NoArgsConstructor;import java。io。Serializable;import java。util。List;@Data@NoArgsConstructor@AllArgsConstructorpublic class Order implements Serializable { private Integer id; private String orderNo; private String orderAddress; private Double totalPrice; private List productList;}

服務介面

package com。example。product。service;import com。example。product。pojo。Product;import java。util。List;/** * 商品服務 */public interface ProductService { /** * 查詢商品列表 * * @return */ List 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 伺服器地址,單機版# Dubbodubbo: # 提供方應用資訊,用於計算依賴關係 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 selectProductList() { log。info(“商品服務查詢商品資訊。。。”); return Arrays。asList( new Product(1, “華為手機”, 1, 5800D), new Product(2, “聯想筆記本”, 1, 6888D), new Product(3, “小米平板”, 5, 2020D) ); }}

值得注意的是

@Service

註解不是 Spring 的註解而是

Dubbo

的註釋:

DubboRPC通訊

啟動類

啟動類透過

@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@SpringBootApplicationpublic 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 伺服器地址,單機版# Dubbodubbo: # 消費方應用名,用於計算依賴關係,不是匹配條件,不要與提供方一樣 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@Servicepublic 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@SpringBootApplicationpublic class OrderServiceApplication { public static void main(String[] args) { SpringApplication。run(OrderServiceApplication。class, args); }}

測試

先啟動 Nacos 伺服器,然後啟動服務提供者 product-service,訪問:http://localhost:8848/nacos/ 控制檯顯示如下:

DubboRPC通訊

DubboRPC通訊

然後啟動服務消費者,控制檯顯示如下:

DubboRPC通訊

訂單服務呼叫遠端商品服務,結果如下:

DubboRPC通訊

Dubbo 負載均衡

在叢集負載均衡時,Dubbo 提供了多種均衡策略,預設為

random

隨機呼叫,也可以自行擴充套件負載均衡策略。

負載均衡策略

Random LoadBalance

隨機

,按權重設定隨機機率。

在一個截面上碰撞的機率高,但呼叫量越大分佈越均勻,而且按機率使用權重後也比較均勻,有利於動態調整提供者權重。

DubboRPC通訊

RoundRobin LoadBalance

輪詢

,按公約後的權重設定輪詢比率。

存在慢的提供者累積請求的問題,比如:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,久而久之,所有請求都卡在調到第二臺上。

LeastActive LoadBalance

最少活躍呼叫數

,ping 值(延遲低)的呼叫,相同延遲的情況下隨機。

使慢的提供者收到更少請求,因為越慢的提供者的呼叫前後計數差會越大。

DubboRPC通訊

ConsistentHash LoadBalance

一致性 Hash

,相同引數的請求總是發到同一提供者。

當某一臺提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。

演算法參見:http://en。wikipedia。org/wiki/Consistent_hashing

預設只對第一個引數 Hash,如果要修改,請配置

預設用 160 份虛擬節點,如果要修改,請配置

配置

一般在專案中不會在程式碼層面指定權重,而是透過監控中心(dubbo-admin)對服務動態的指定權重,

xml

服務端服務級別

客戶端服務級別

服務端方法級別

客戶端方法級別

yaml

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

註解

@Service(loadbalance = “roundrobin”, weight = 100)@Reference(loadbalance = “roundrobin”)

DubboRPC通訊

DubboRPC通訊

至此 Dubbo RPC 通訊所有的知識點就講解結束了。

stent_hashing - 預設只對第一個引數 Hash,如果要修改,請配置

- 預設用 160 份虛擬節點,如果要修改,請配置

配置

一般在專案中不會在程式碼層面指定權重,而是透過監控中心(dubbo-admin)對服務動態的指定權重

xml

服務端服務級別

客戶端服務級別

服務端方法級別

客戶端方法級別

yaml

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

註解

@Service(loadbalance = “roundrobin”, weight = 100)@Reference(loadbalance = “roundrobin”)

DubboRPC通訊

DubboRPC通訊

至此 Dubbo RPC 通訊所有的知識點就講解結束了。