Dubbo 介紹

官網(wǎng):http://dubbo.apache.org/zh-cn/
Github:https://github.com/apache/dubbo
2018 年 2 月 15 日,阿里巴巴的服務(wù)治理框架 dubbo 通過投票,順利成為 Apache 基金會(huì)孵化項(xiàng)目。
Apache Dubbo 是一款高性能、輕量級(jí)的開源 Java RPC 框架,它提供了三大核心能力:面向接口的遠(yuǎn)程方法調(diào)用,智能容錯(cuò)和負(fù)載均衡,以及服務(wù)自動(dòng)注冊和發(fā)現(xiàn)。
Dubbo 架構(gòu)
Dubbo 提供三個(gè)核心功能:基于接口的遠(yuǎn)程調(diào)用、容錯(cuò)和負(fù)載均衡,以及服務(wù)的自動(dòng)注冊與發(fā)現(xiàn)。Dubbo 框架廣泛的在阿里巴巴內(nèi)部使用,以及當(dāng)當(dāng)、去哪兒、網(wǎng)易考拉、滴滴等都在使用。

節(jié)點(diǎn)角色說明
| 節(jié)點(diǎn) | 角色說明 |
|---|---|
Provider |
暴露服務(wù)的服務(wù)提供方 |
Consumer |
調(diào)用遠(yuǎn)程服務(wù)的服務(wù)消費(fèi)方 |
Registry |
服務(wù)注冊與發(fā)現(xiàn)的注冊中心 |
Monitor |
統(tǒng)計(jì)服務(wù)的調(diào)用次數(shù)和調(diào)用時(shí)間的監(jiān)控中心 |
Container |
服務(wù)運(yùn)行容器 |
調(diào)用關(guān)系說明
- 服務(wù)容器負(fù)責(zé)啟動(dòng),加載,運(yùn)行服務(wù)提供者。
- 服務(wù)提供者在啟動(dòng)時(shí),向注冊中心注冊自己提供的服務(wù)。
- 服務(wù)消費(fèi)者在啟動(dòng)時(shí),向注冊中心訂閱自己所需的服務(wù)。
- 注冊中心返回服務(wù)提供者地址列表給消費(fèi)者,如果有變更,注冊中心將基于長連接推送變更數(shù)據(jù)給消費(fèi)者。
- 服務(wù)消費(fèi)者,從提供者地址列表中,基于軟負(fù)載均衡算法,選一臺(tái)提供者進(jìn)行調(diào)用,如果調(diào)用失敗,再選另一臺(tái)調(diào)用。
- 服務(wù)消費(fèi)者和提供者,在內(nèi)存中累計(jì)調(diào)用次數(shù)和調(diào)用時(shí)間,定時(shí)每分鐘發(fā)送一次統(tǒng)計(jì)數(shù)據(jù)到監(jiān)控中心。
Dubbo 快速入門
我們先通過一個(gè)簡單的案例讓大家理解一下 Dubbo 的使用,然后基于 Spring Boot 和 Spring Cloud 環(huán)境整合 Dubbo。
Dubbo 采用全 Spring 配置方式,透明化接入應(yīng)用,對應(yīng)用沒有任何 API 侵入,只需用 Spring 加載 Dubbo 的配置即可。
依賴
JDK 1.6 以上和 Maven 3.0 以上,采用 Maven 多模塊聚合工程構(gòu)建 api 模塊,provider 模塊以及 consumer 模塊。
聚合工程
項(xiàng)目結(jié)構(gòu)如下圖,簡單介紹一下:
-
dubbo-api:服務(wù)接口 -
dubbo-provider:依賴服務(wù)接口,具體的業(yè)務(wù)實(shí)現(xiàn),服務(wù)提供者 -
dubbo-coonsumer:依賴服務(wù)接口,遠(yuǎn)程調(diào)用服務(wù),服務(wù)消費(fèi)者

依賴關(guān)系
dubbo-parent 的 pom.xml 依賴 apache dubbo。
<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 服務(wù)接口。
<dependencies>
<!-- dubbo-api 依賴 -->
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
定義服務(wù)接口
dubbo-api 中編寫 HelloService.java
package org.example.service;
/**
* Hello服務(wù)
*/
public interface HelloService {
String sayHello(String name);
}
定義服務(wù)提供者
在 provider 模塊中實(shí)現(xiàn)服務(wù)接口
dubbo-provider 中編寫 HelloServiceImpl.java
package org.example.service.impl;
import org.example.service.HelloService;
/**
* 服務(wù)實(shí)現(xiàn)
*/
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello " + name;
}
}
配置服務(wù)提供者
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">
<!-- 提供方應(yīng)用信息,用于計(jì)算依賴關(guān)系 -->
<dubbo:application name="hello-world-app"/>
<!-- 使用 multicast 廣播注冊中心暴露服務(wù)地址 -->
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<!-- 用 dubbo 協(xié)議在 20880 端口暴露服務(wù) -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 聲明需要暴露的服務(wù)接口 -->
<dubbo:service interface="org.example.service.HelloService" ref="helloService"/>
<!-- 和本地 bean 一樣實(shí)現(xiàn)服務(wù) -->
<bean id="helloService" class="org.example.service.impl.HelloServiceImpl"/>
</beans>
加載 Spring 配置啟動(dòng)服務(wù)
package org.example;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 發(fā)布服務(wù)
*/
public class Provider {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:dubbo-provider.xml");
context.start();
System.out.println("服務(wù)注冊成功!");
System.in.read(); // 按任意鍵退出
}
}
定義服務(wù)消費(fèi)者
通過 Spring 配置引用遠(yuǎn)程服務(wù)
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">
<!-- 消費(fèi)方應(yīng)用名,用于計(jì)算依賴關(guān)系,不是匹配條件,不要與提供方一樣 -->
<dubbo:application name="consumer-of-helloworld-app" />
<!-- 使用 multicast 廣播注冊中心暴露發(fā)現(xiàn)服務(wù)地址 -->
<dubbo:registry address="multicast://224.5.6.7:1234" />
<!-- 生成遠(yuǎn)程服務(wù)代理,可以和本地 bean 一樣使用 helloService -->
<dubbo:reference id="helloService" interface="org.example.service.HelloService" />
</beans>
加載 Spring 配置并調(diào)用遠(yuǎn)程服務(wù)
package org.example;
import org.example.service.HelloService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 調(diào)用遠(yuǎn)程服務(wù)
*/
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"); // 獲取遠(yuǎn)程服務(wù)代理
String result = helloService.sayHello("world"); // 執(zhí)行遠(yuǎn)程方法
System.out.println(result); // 顯示調(diào)用結(jié)果
}
}
Dubbo 常用標(biāo)簽
-
dubbo:application:應(yīng)用程序名稱 -
dubbo:registry:連接注冊中心信息(配置注冊中心) -
dubbo:protocol:服務(wù)提供者注冊服務(wù)采用的協(xié)議- Dubbo 協(xié)議,默認(rèn)
- RMI 協(xié)議
- Hessian 協(xié)議
- HTTP 協(xié)議
- WebService 協(xié)議
- Thrift 協(xié)議
- Memcached 協(xié)議
- Redis 協(xié)議
- Rest 協(xié)議(RESTful)
- Grpc 協(xié)議
- 更多協(xié)議信息請參考:http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html
-
dubbo:service:聲明需要暴露的服務(wù)接口 -
dubbo:reference:配置訂閱的服務(wù)(生成遠(yuǎn)程服務(wù)代理)
更多配置信息請參考:http://dubbo.apache.org/zh-cn/docs/user/references/xml/introduction.html
注冊中心
注冊中心我們已經(jīng)學(xué)習(xí)了不少,例如:ZooKeeper、Eureka、Consul、Nacos 等等,注冊中心可以更高效的管理系統(tǒng)的服務(wù):比如服務(wù)接口的發(fā)布、自動(dòng)剔除無效的服務(wù)、自動(dòng)恢復(fù)服務(wù)等。
Dubbo 支持五種注冊中心:Multicast、Nacos(推薦)、ZooKeeper(推薦) 、Redis、Simple。本文重點(diǎn)介紹前兩個(gè),更多注冊中心的信息請參考:http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html
Multicast 注冊中心
Multicast 注冊中心不需要啟動(dòng)任何中心節(jié)點(diǎn),只要廣播地址一樣,就可以互相發(fā)現(xiàn)。

- 提供方啟動(dòng)時(shí)廣播自己的地址
- 消費(fèi)方啟動(dòng)時(shí)廣播訂閱請求
- 提供方收到訂閱請求時(shí),單播自己的地址給訂閱者,如果設(shè)置了
unicast=false,則廣播給訂閱者 - 消費(fèi)方收到提供方地址時(shí),連接該地址進(jìn)行 RPC 調(diào)用。
組播受網(wǎng)絡(luò)結(jié)構(gòu)限制,只適合小規(guī)模應(yīng)用或開發(fā)階段使用。組播地址段: 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 缺省使用單播發(fā)送提供者地址信息給消費(fèi)者,如果一個(gè)機(jī)器上同時(shí)啟了多個(gè)消費(fèi)者進(jìn)程,消費(fèi)者需聲明 unicast=false,否則只會(huì)有一個(gè)消費(fèi)者能收到消息。
當(dāng)服務(wù)者和消費(fèi)者運(yùn)行在同一臺(tái)機(jī)器上,消費(fèi)者同樣需要聲明unicast=false,否則消費(fèi)者無法收到消息,導(dǎo)致 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 注冊中心
Apache ZooKeeper 是一個(gè)開放源碼的分布式應(yīng)用程序協(xié)調(diào)組件,是 Hadoop 和 Hbase 的重要組件。它是一個(gè)為分布式應(yīng)用提供一致性服務(wù)的軟件,提供的功能包括:配置維護(hù)、域名服務(wù)、分布式同步、組服務(wù)等。適合作為 Dubbo 服務(wù)的注冊中心,工業(yè)強(qiáng)度較高,可用于生產(chǎn)環(huán)境,推薦使用。
在微服務(wù)項(xiàng)目開發(fā)中 ZooKeeper 主要的角色是當(dāng)做服務(wù)注冊中心存在,我們將編寫好的服務(wù)注冊至 ZooKeeper 即可。

流程說明:
- 服務(wù)提供者啟動(dòng)時(shí): 向
/dubbo/com.foo.BarService/providers目錄下寫入自己的 URL 地址。 - 服務(wù)消費(fèi)者啟動(dòng)時(shí): 訂閱
/dubbo/com.foo.BarService/providers目錄下的提供者 URL 地址。并向/dubbo/com.foo.BarService/consumers目錄下寫入自己的 URL 地址。 - 監(jiān)控中心啟動(dòng)時(shí): 訂閱
/dubbo/com.foo.BarService目錄下的所有提供者和消費(fèi)者 URL 地址。
支持以下功能:
- 當(dāng)提供者出現(xiàn)斷電等異常停機(jī)時(shí),注冊中心能自動(dòng)刪除提供者信息;
- 當(dāng)注冊中心重啟時(shí),能自動(dòng)恢復(fù)注冊數(shù)據(jù),以及訂閱請求;
- 當(dāng)會(huì)話過期時(shí),能自動(dòng)恢復(fù)注冊數(shù)據(jù),以及訂閱請求;
- 當(dāng)設(shè)置
<dubbo:registry check="false" />時(shí),記錄失敗注冊和訂閱請求,后臺(tái)定時(shí)重試; - 可通過
<dubbo:registry username="admin" password="1234" />設(shè)置 zookeeper 登錄信息; - 可通過
<dubbo:registry group="dubbo" />設(shè)置 zookeeper 的根節(jié)點(diǎn),不配置將使用默認(rèn)的根節(jié)點(diǎn); - 支持
*號(hào)通配符<dubbo:reference group="*" version="*" />,可訂閱服務(wù)的所有分組和所有版本的提供者。
作為 Dubbo 的老牌黃金搭檔 ZooKeeper,我們在單獨(dú)講解 Dubbo 時(shí)已經(jīng)給大家分享過如何使用了,本文系 Spring Cloud Alibaba 系列文章,重點(diǎn)對象是 Nacos,所以 ZooKeeper 這里就不過多贅述了。
Nacos 注冊中心
Nacos 是 Alibaba 公司推出的開源工具,用于實(shí)現(xiàn)分布式系統(tǒng)的服務(wù)發(fā)現(xiàn)與配置管理。Nacos 是 Dubbo 生態(tài)系統(tǒng)中重要的注冊中心實(shí)現(xiàn),其中 dubbo-registry-nacos 則是 Dubbo 融合 Nacos 注冊中心的實(shí)現(xiàn),推薦使用。
Nacos 官網(wǎng):https://nacos.io/zh-cn/
Github:https://github.com/alibaba/nacos
預(yù)備工作
當(dāng)您將 Nacos 整合到您的 Dubbo 工程之前,請確保后臺(tái)已經(jīng)啟動(dòng) Nacos 服務(wù)。關(guān)于 Nacos 的安裝和其他詳細(xì)內(nèi)容可參考我之前的文章 Spring Cloud 系列之 Alibaba Nacos 注冊中心(一)、Spring Cloud 系列之 Alibaba Nacos 注冊中心(二)。
快速上手
Dubbo 融合 Nacos 成為注冊中心的操作步驟非常簡單,大致步驟可分為“增加 Maven 依賴”和“配置注冊中心“。
依賴
核心依賴主要是 dubbo-registry-nacos 和 nacos-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.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>
配置注冊中心
服務(wù)提供者和服務(wù)消費(fèi)者只需要調(diào)整 address 屬性配置即可。
單機(jī)配置:
<!-- 使用 Nacos 注冊中心,單機(jī)版 -->
<dubbo:registry address="nacos://127.0.0.1:8848"/>
<!-- 或 -->
<dubbo:registry protocol="nacos" address="127.0.0.1:2181"/>
或:
dubbo.register.address=nacos://192.168.10.101:8848
或:
dubbo.register.protocol=nacos
dubbo.register.address=192.168.10.101:8848
集群配置:
<!-- 使用 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.register.address=nacos://192.168.10.101:8848
或:
dubbo.register.protocol=nacos
dubbo.register.address=192.168.10.101:8848,192.168.10.102:8848,192.168.10.103:8848
隨后,重啟您的 Dubbo 應(yīng)用,Dubbo 的服務(wù)提供和消費(fèi)信息在 Nacos 控制臺(tái)中即可顯示。
Spring Cloud Alibaba Nacos 整合 Dubbo
之前的文章中,無論我們學(xué)習(xí) Eureka、Consul 還是 Nacos,負(fù)責(zé)服務(wù)間通信的功能都是由 Ribbon 來完成的,接下來我們使用 Dubbo 來替換 Ribbon。
聚合工程
dubbo-demo 聚合工程。SpringBoot 2.3.0.RELEASE、Spring Cloud Hoxton.SR5。
項(xiàng)目結(jié)構(gòu)如下圖,簡單介紹一下:
-
service-api:服務(wù)接口 -
product-service:商品服務(wù),服務(wù)提供者,提供了/product/list接口 -
order-service:訂單服務(wù),服務(wù)消費(fèi)者,遠(yuǎn)程調(diào)用商品服務(wù)

依賴關(guān)系
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>
<!-- 項(xiàng)目坐標(biāo)地址 -->
<groupId>com.example</groupId>
<!-- 項(xiàng)目模塊名稱 -->
<artifactId>dubbo-demo</artifactId>
<packaging>pom</packaging>
<!-- 項(xiàng)目版本名稱 快照版本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 依賴 -->
<!-- 使用繼承方式,實(shí)現(xiàn)復(fù)用,符合繼承的都可以被使用 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
</parent>
<!--
集中定義依賴組件版本號(hào),但不引入,
在子工程中用到聲明的依賴時(shí),可以不加依賴的版本號(hào),
這樣可以統(tǒng)一管理工程中用到的依賴版本
-->
<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>
<!-- 項(xiàng)目依賴管理 父項(xiàng)目只是聲明依賴,子項(xiàng)目需要寫明需要的依賴(可以省略版本信息) -->
<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>
<!-- 項(xiàng)目依賴 -->
<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>
<!-- 項(xiàng)目依賴 -->
<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>
定義服務(wù)接口
我們在 service-api 模塊中定義實(shí)體類和服務(wù)接口信息。
實(shí)體類
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;
}
服務(wù)接口
package com.example.product.service;
import com.example.product.pojo.Product;
import java.util.List;
/**
* 商品服務(wù)
*/
public interface ProductService {
/**
* 查詢商品列表
*
* @return
*/
List<Product> selectProductList();
}
定義服務(wù)提供者
配置文件
配置文件需要配置 Nacos 注冊中心和 Dubbo 相關(guān)信息,核心配置如下:
server:
port: 7070 # 端口
spring:
application:
name: product-service # 應(yīng)用名稱
# 配置 Nacos 注冊中心
cloud:
nacos:
discovery:
enabled: true # 如果不想使用 Nacos 進(jìn)行服務(wù)注冊和發(fā)現(xiàn),設(shè)置為 false 即可
server-addr: 127.0.0.1:8848 # Nacos 服務(wù)器地址,單機(jī)版
# Dubbo
dubbo:
# 提供方應(yīng)用信息,用于計(jì)算依賴關(guān)系
application:
name: product-service
# 使用 nacos 注冊中心暴露服務(wù)地址
registry:
protocol: nacos
address: spring-cloud://localhost
# 用 dubbo 協(xié)議在 20880 端口暴露服務(wù)
protocol:
name: dubbo
port: 20880
# 掃描需要暴露的服務(wù),可以被 @EnableDubbo 注解替代
#scan:
# base-packages: com.example.service
服務(wù)提供者
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;
/**
* 商品服務(wù)
* timeout 調(diào)用該服務(wù)的超時(shí)時(shí)間
* version 為版本號(hào)
* group 為分組
* interface、group、version 三者確定一個(gè)服務(wù)
*/
@Slf4j
@Service(timeout = 5000, version = "1.0", group = "product-service")
public class ProductServiceImpl implements ProductService {
/**
* 查詢商品列表
*
* @return
*/
@Override
public List<Product> selectProductList() {
log.info("商品服務(wù)查詢商品信息...");
return Arrays.asList(
new Product(1, "華為手機(jī)", 1, 5800D),
new Product(2, "聯(lián)想筆記本", 1, 6888D),
new Product(3, "小米平板", 5, 2020D)
);
}
}
值得注意的是 @Service 注解不是 Spring 的注解而是 Dubbo 的注釋:

啟動(dòng)類
啟動(dòng)類通過 @EnableDubbo 注解掃描需要暴露的服務(wù),如果配置文件中配置了該選項(xiàng),那么這里可以省略。
package com.example;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 掃描需要暴露的服務(wù)
@EnableDubbo(scanBasePackages = "com.example.service")
// 開啟 @EnableDiscoveryClient 注解,當(dāng)前版本默認(rèn)會(huì)開啟該注解
//@EnableDiscoveryClient
@SpringBootApplication
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}
定義服務(wù)消費(fèi)者
配置文件
配置文件需要配置 Nacos 注冊中心和 Dubbo 相關(guān)信息,核心配置如下:
server:
port: 9090 # 端口
spring:
application:
name: order-service # 應(yīng)用名稱
# 配置 Nacos 注冊中心
cloud:
nacos:
discovery:
enabled: true # 如果不想使用 Nacos 進(jìn)行服務(wù)注冊和發(fā)現(xiàn),設(shè)置為 false 即可
server-addr: 127.0.0.1:8848 # Nacos 服務(wù)器地址,單機(jī)版
# Dubbo
dubbo:
# 消費(fèi)方應(yīng)用名,用于計(jì)算依賴關(guān)系,不是匹配條件,不要與提供方一樣
application:
name: order-service
# 發(fā)現(xiàn) nacos 注冊中心暴露的服務(wù)
registry:
protocol: nacos
address: spring-cloud://localhost
cloud:
subscribed-services: product-service # 訂閱服務(wù),遠(yuǎn)程調(diào)用的服務(wù)名稱
服務(wù)消費(fèi)者
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 注解,用于引入遠(yuǎn)程服務(wù)
// 如果注冊服務(wù)時(shí)設(shè)置了版本及分組信息,調(diào)用遠(yuǎn)程服務(wù)時(shí)也要設(shè)置對應(yīng)的版本及分組信息
@Reference(timeout = 5000, version = "1.0", group = "product-service")
private ProductService productService;
/**
* 根據(jù)主鍵查詢訂單
*
* @param id
* @return
*/
@Override
public Order selectOrderById(Integer id) {
log.info("訂單服務(wù)查詢訂單信息...");
return new Order(id, "order-001", "中國", 22788D,
productService.selectProductList());
}
}
啟動(dòng)類
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 開啟 @EnableDiscoveryClient 注解,當(dāng)前版本默認(rèn)會(huì)開啟該注解
//@EnableDiscoveryClient
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
測試
先啟動(dòng) Nacos 服務(wù)器,然后啟動(dòng)服務(wù)提供者 product-service,訪問:http://localhost:8848/nacos/ 控制臺(tái)顯示如下:


然后啟動(dòng)服務(wù)消費(fèi)者,控制臺(tái)顯示如下:

訂單服務(wù)調(diào)用遠(yuǎn)程商品服務(wù),結(jié)果如下:

Dubbo 負(fù)載均衡
在集群負(fù)載均衡時(shí),Dubbo 提供了多種均衡策略,缺省為 random 隨機(jī)調(diào)用,也可以自行擴(kuò)展負(fù)載均衡策略。
負(fù)載均衡策略
Random LoadBalance
- 隨機(jī),按權(quán)重設(shè)置隨機(jī)概率。
- 在一個(gè)截面上碰撞的概率高,但調(diào)用量越大分布越均勻,而且按概率使用權(quán)重后也比較均勻,有利于動(dòng)態(tài)調(diào)整提供者權(quán)重。

RoundRobin LoadBalance
- 輪詢,按公約后的權(quán)重設(shè)置輪詢比率。
- 存在慢的提供者累積請求的問題,比如:第二臺(tái)機(jī)器很慢,但沒掛,當(dāng)請求調(diào)到第二臺(tái)時(shí)就卡在那,久而久之,所有請求都卡在調(diào)到第二臺(tái)上。
LeastActive LoadBalance
- 最少活躍調(diào)用數(shù),ping 值(延遲低)的調(diào)用,相同延遲的情況下隨機(jī)。
- 使慢的提供者收到更少請求,因?yàn)樵铰奶峁┱叩恼{(diào)用前后計(jì)數(shù)差會(huì)越大。

ConsistentHash LoadBalance
- 一致性 Hash,相同參數(shù)的請求總是發(fā)到同一提供者。
- 當(dāng)某一臺(tái)提供者掛時(shí),原本發(fā)往該提供者的請求,基于虛擬節(jié)點(diǎn),平攤到其它提供者,不會(huì)引起劇烈變動(dòng)。
- 算法參見:http://en.wikipedia.org/wiki/Consistent_hashing
- 缺省只對第一個(gè)參數(shù) Hash,如果要修改,請配置
<dubbo:parameter key="hash.arguments" value="0,1" /> - 缺省用 160 份虛擬節(jié)點(diǎn),如果要修改,請配置
<dubbo:parameter key="hash.nodes" value="320" />
配置
一般在項(xiàng)目中不會(huì)在代碼層面指定權(quán)重,而是通過監(jiān)控中心(dubbo-admin)對服務(wù)動(dòng)態(tài)的指定權(quán)重,官方文檔:http://dubbo.apache.org/zh-cn/docs/admin/introduction.html
xml
服務(wù)端服務(wù)級(jí)別
<dubbo:service interface="..." loadbalance="roundrobin" weight="100" />
客戶端服務(wù)級(jí)別
<dubbo:reference interface="..." loadbalance="roundrobin" />
服務(wù)端方法級(jí)別
<dubbo:service interface="..." weight="100">
<dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:service>
客戶端方法級(jí)別
<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 通信所有的知識(shí)點(diǎn)就講解結(jié)束了。
本文采用 <a target="_blank">知識(shí)共享「署名-非商業(yè)性使用-禁止演繹 4.0 國際」許可協(xié)議</a>。
大家可以通過 <a target="_blank">分類</a> 查看更多關(guān)于 <a target="_blank">Spring Cloud</a> 的文章。
?? 您的點(diǎn)贊和轉(zhuǎn)發(fā)是對我最大的支持。
?? 關(guān)注公眾號(hào) 哈嘍沃德先生「文檔 + 視頻」每篇文章都配有專門視頻講解,學(xué)習(xí)更輕松噢 ~