1、介紹
Dubbo官方文檔:https://dubbo.apache.org/zh
目前Dubbo最新的是Dubbo3。本文使用的是 2.7.3 (推薦)
Dubbo3 格式的 Provider 地址不能被 Dubbo2 的 Consumer 識別到,反之 Dubbo2 的消費者也不能訂閱到 Dubbo3 Provider。
這里的架構和使用都是基於Dubbo2.7 版本,Dubbo2(2.7以下)和Dubbo3兩個版本的注解有區別。
Registry是注冊中心,用於發現服務者和消費者。
注冊中心可以選擇 zookeeper、consul、nacos,推薦使用zookeeper。
dubboRPC通信的原理:
dubbo和springCloud的區別:
2、架構
Dubbo三大組件:
- 注冊中心。協調 Consumer 與 Provider 之間的地址注冊與發現
- 配置中心。
- 存儲 Dubbo 啟動階段的全局配置,保證配置的跨環境共享與全局一致性
- 負責服務治理規則(路由規則、動態配置等)的存儲與推送。
- 元數據中心。
- 接收 Provider 上報的服務接口元數據,為 Admin 等控制台提供運維能力(如服務測試、接口文檔等)
- 作為服務發現機制的補充,提供額外的接口/方法級別配置信息的同步能力,相當於注冊中心的額外擴展
以上三個中心並不是運行 Dubbo 的必要條件,用戶完全可以根據自身業務情況決定只啟用其中一個或多個,以達到簡化部署的目的。
通常情況下,所有用戶都會以獨立的注冊中心 開始 Dubbo 服務開發,而配置中心、元數據中心則會在微服務演進的過程中逐步的按需被引入進來。
3、使用
依賴:(推薦使用2.7.13版本)。
spring集成dubbo:
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.13</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>2.7.13</version>
</dependency>
springboot與dubbo集成:
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.13</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>2.7.13</version>
</dependency>
3.1、spring.xml配置
1、服務端
在服務提供方實現接口(對服務消費方隱藏實現):
spring-provider.xml :
<!--注意:配置集群的情況下需要同一集群的name值相同-->
<dubbo:application name="demo-provider" />
<!-- 使用zookeeper注冊中心暴露服務地址 -->
<dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181" />
<!--實際項目中使用properties文件的形式定義zookeeper的地址 -->
<!-- <dubbo:registry protocol="zookeeper" address="${zookeeper.address}" check="false" file="dubbo.properties" /> -->
<dubbo:provider token="true" />
<!-- beanId -->
<bean id="demoService" class="org.apache.dubbo.samples.basic.impl.DemoServiceImpl"/>
<!--用 Spring 配置聲明暴露服務,ref="demoService" 是 beanId,必填 -->
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService" />
<!--使用dubbo協議,聲明服務的端口-->
<dubbo:protocol name="dubbo" port="20898"/>
一般來說,我們會把 beanId 交給 spring 去管理只需要在 xml 里面聲明掃描包,然后使用 @Service
聲明實現類 即可:
<context:component-scan base-package="org.apache.dubbo.samples.basic.impl"/>
2、客戶端
spring-consumer.xml:
<dubbo:application name="demo-consumer"/>
<dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
<!-- 服務方引用的beanId -->
<dubbo:reference id="demoService" check="true" interface="org.apache.dubbo.samples.basic.api.DemoService"/>
3.2、springboot配置
1、服務端
上面例子的 spring-provider.xml 換成 properties 文件的寫法是這樣的:
# 應用名
dubbo.application.name=demo-provider
# 注冊中心地址
dubbo.registry.address=zookeeper://localhost:2181
# 調用協議地址
dubbo.protocol.name=dubbo
dubbo.protocol.port=28080
#開啟包掃描,可替代 @EnableDubbo 注解
#dubbo.scan.base-packages=com.meizu.quickgame
開啟基於注解的dubbo功能(主要是包掃描@DubboComponentScan
):
// 開啟基於注解的dubbo功能(主要是包掃描@DubboComponentScan)
// 也可以在配置文件中使用dubbo.scan.base-package來替代@EnableDubbo
@EnableDubbo(scanBasePackages = {"com.meizu.quickgame"})
@SpringBootApplication
public class UserServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceProviderApplication.class, args);
}
}
2、客戶端
# 應用名
dubbo.application.name=demo-consumer
# 注冊中心地址
dubbo.registry.address=zookeeper://localhost:2181
dubbo.consumer.timeout=1000
使用例子1:
dubbo和spring可以完美結合,只需要在spring配置文件聲明即可。
provider:
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-demo-provider.xml");
context.start();
new CountDownLatch(1).await();
}
consumer:
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-demo-consumer.xml");
context.start();
DemoService demoService = (DemoService) context.getBean("demoService");
String hello = demoService.sayHello("world");
}
使用例子2:
這里展示注解的方式,需要聲明@EnableDubbo
provider:
@DubboService(version = "1.0.0")
public class UserServiceImpl implements UserService{
}
consumer:
@DubboReference(version = "*", protocol = "dubbo,hessian", loadbalance = "random" )
private UserService userService;
4、其他
4.1、負載均衡:
xml:
服務端級別:
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService" loadbalance="roundrobin" timeout="5000"/>
服務端方法級別:
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService" timeout="5000">
<dubbo:method name="sayHello" loadbalance="roundrobin"/>
</dubbo:service>
客戶端級別:
<dubbo:reference id="demoService" check="true" interface="org.apache.dubbo.samples.basic.api.DemoService" loadbalance="roundrobin"/>
客戶端方法級別:
<dubbo:reference id="demoService" check="true" interface="org.apache.dubbo.samples.basic.api.DemoService">
<dubbo:method name="sayHello" loadbalance="roundrobin"/>
</dubbo:reference>
當多個provider注冊到zk,consumer會選擇指定的負載均衡算法自動請求,遇到上線或者下線會重新計算。
常見有四種負載均衡:
- random
隨機,按權重設置隨機概率。
在一個截面上碰撞的概率高,但調用量越大分布越均勻,而且按概率使用權重后也比較均勻,有利於動態調整提供者權重。
- roundRobin (默認)
輪詢,按公約后的權重設置輪詢比率。
存在慢的提供者類即請求問題,比如:第二台機器很慢,但沒掛,當請求調到第二台時就卡在那,久而久之,所有請求都卡在調到第二台上。
- leastActive
最少活躍調用數,相同活躍數的隨機,活躍數指調用前后計數差。
使慢的提供者收到更少請求,因為越慢的提供者的調用前后計數差會越大。
- consistentHash
一致性Hash,相同參數的請求總是發到同一提供者。
當某一台提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其他提供者,不會引起劇烈變動
4.2多協議:
dubbo支持多種協議:
- dubbo
- hessian
- http
- rmi
- webservice
- thrift
- memcached
- redis
不同服務不同協議
不同服務在性能上適用不同協議進行傳輸,比如大數據用短連接協議,小數據大並發用長連接協議
<?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="world" />
<dubbo:registry id="registry" address="10.20.141.150:9090" username="admin" password="hello1234" />
<!-- 多協議配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<!-- 使用dubbo協議暴露服務 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" protocol="dubbo" />
<!-- 使用rmi協議暴露服務 -->
<dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" protocol="rmi" />
</beans>
多協議暴露服務
需要與 http 客戶端相互操作
<?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="world" />
<dubbo:registry id="registry" address="10.20.141.150:9090" username="admin" password="hello1234" />
<!-- 多協議配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="hessian" port="8080" />
<!-- 使用多個協議暴露服務 -->
<dubbo:service id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" protocol="dubbo,hessian" />
</beans>
4.3、多注冊中心
兩個不同注冊中心,使用豎號分隔多個不同注冊中心地址:
<!-- 多注冊中心配置,豎號分隔表示同時連接多個不同注冊中心,同一注冊中心的多個集群地址用逗號分隔 -->
<dubbo:registry address="10.20.141.150:9090|10.20.154.177:9010" />
<!-- 引用服務 -->
<dubbo:reference id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" />
不同服務使用不同注冊中心:
<!-- 多注冊中心配置 -->
<dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
<!-- 向中文站注冊中心注冊 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="chinaRegistry" />
<!-- 向國際站注冊中心注冊 -->
<dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" registry="intlRegistry" />
還可以這樣:
<!-- 多注冊中心配置 -->
<dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
<!-- 引用中文站服務 -->
<dubbo:reference id="chinaHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="chinaRegistry" />
<!-- 引用國際站站服務 -->
<dubbo:reference id="intlHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="intlRegistry" />
4.4、超時、重試
超時默認重試 值: 1000ms
重試默認重試2次,不算第一個調用,一共會調用三次
如服務方設置:
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService2" loadbalance="roundrobin" timeout="500" retries="1"/>
注意,本人測試了一下,雖然服務方設置重試次數為1,消費者默認,最終結果還是會重試2次(消費者默認值),所以服務方提供的重試次數無效,建議在消費者設置,但是超時時間是生效的。
超時、重試建議在消費者設置:
<dubbo:reference id="demoService01" check="true" interface="org.apache.dubbo.samples.basic.api.DemoService"
retries="1" timeout="3000"/>
4.5、分組
當一個接口有多種實現時,可以用 group 區分。
provider.xml
<dubbo:service id="groupADubboService" group="groupA" interface="org.apache.dubbo.samples.group.api.GroupService"
ref="groupAService"/>
<dubbo:service id="groupBDubboService" group="groupB" interface="org.apache.dubbo.samples.group.api.GroupService"
ref="groupBService"/>
consumer.xml
<!-- * 表示 任意組-->
<dubbo:reference group="*" id="groupAService" check="false"
interface="org.apache.dubbo.samples.group.api.GroupService" />
<!-- 調用groupB接口 -->
<!--<dubbo:reference group="groupB" id="groupBService" check="false"-->
<!--interface="org.apache.dubbo.samples.group.api.GroupService"/>-->
group="*"
和 group="groupA,groupB"
是一樣的效果,但總是只調一個可用組的實現。(不能實現負載均衡)
這里有個坑,reference group="*" ,consumer在啟動的時候已經選好了分組,並不是輪詢兩個分組
如果你要使用group進行負載均衡,consumer就不要寫group
4.6、版本
當一個接口實現,出現不兼容升級時,可以用版本號過渡,版本號不同的服務相互間不引用。
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
<dubbo:service interface="com.foo.BarService" version="1.0.0" />
consumer 設置 version="*" 可以 輪詢 調用provider,而group不能,這是和group的最大區別
4.6、直連模式
直連模式一般應用於測試模式。
消費者:
<dubbo:registry id="na" address="N/A" />
<!--使用直連的方式,不需要注冊中心 這里的check表示開啟token-->
<dubbo:reference id="demoService01" check="true" interface="org.apache.dubbo.samples.basic.api.DemoService"
url="dubbo://172.16.44.48:20897"
protocol="dubbo" registry="na"
timeout="3000"
retries="3"
/>
服務方不需要修改,如果開啟了token驗證,需要在<dubbo:provider>
或者 <dubbo:service>
聲明:
<dubbo:registry id="default" address="zookeeper://${zookeeper.address:127.0.0.1}:2181" />
<!--使用dubbo協議,聲明服務的端口-->
<dubbo:protocol id="dubbo1" name="dubbo" port="20897" />
<dubbo:protocol id="dubbo2" name="dubbo" port="20898"/>
<dubbo:provider token="123456" registry="default">
<!--用 Spring 配置聲明暴露服務 ref 的 值要等 beanId 的 值-->
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService02" loadbalance="roundrobin"
timeout="5000" retries="1" protocol="dubbo1"/>
</dubbo:provider>
消費者需要攜帶token:
RpcContext.getContext().setAttachment("token","123456");
如果不需要token驗證,設置成 false 即可
5、不同協議的區別
duboo3.0 的默認協議是 dubbo,較2.0版本來說是一個新的協議(官方稱為triple)
dubbo | rmi | hessian | http | webservice | thrift | |
---|---|---|---|---|---|---|
傳輸協議 | TCP | TCP | HTTP | HTTP | HTTP | thrif |
傳輸方式 | NIO異步傳輸 | 同步 | 同步 | 同步 | 同步 | 同步、異步 |
序列化 | Hessian二進制序列化 | Java標准二進制序列化 | Hessian二進制序列化 | 表單序列化 | SOAP文本序列化 | Thrift |
適用范圍 | 傳入傳出參數數據包較小(建議小於100K),消費者比提供者個數多,單一消費者無法壓滿提供者,盡量不要使用dubbo協議傳輸大文件或超大字符串 | 傳入傳出參數數據包大小混合,消費者與提供者個數差不多,可傳文件。 | 傳入傳出參數數據包較大,提供者比消費者個數多,提供者壓力較大,可傳文件。 | 傳入傳出參數數據包大小混合,提供者比消費者個數多,可用瀏覽器查看,可用表單或URL傳入參數,暫不支持傳文件。 | 系統集成,跨語言調用。 | 跨語言 |
約束 | 參數及返回值需實現Serializable接口 | 參數及返回值需實現Serializable接口 | 參數及返回值需實現Serializable接口 | 不能在協議中傳遞null值 |
dubbo 有多種協議,不同的協議默認使用不同的序列化框架。比如:
dubbo 協議 默認使用 Hessian2 序列化。(說明:Hessian2 是阿里在 Hessian 基礎上進行的二次開發,起名為Hessian2 )
rmi協議 默認為 java 原生序列化,
http 協議 默認為 為 json 。
hessian 協議,默認是 hessian 序列化;
webservice 協議,默認是 soap 文本序列化 。
6、注意事項(TODO )
1、使用了dubbo2.7.13 版本,thrift 協議 的name 是native-thrift,而且 RpcContext.getContext().getAttachments
無法獲取參數
當前 dubbo 支持的 thrift 協議是對 thrift 原生協議 的擴展,與原生Thrift不兼容 ,使用的是
protocol="tri"
吐槽一下,dubbo3官方的更新進度也太慢了,本人在使用dubbo 3.0.2.1 的時候,並沒有 triple 協議,官方文檔也沒有找到demo,最后是在dubbo-benchmark項目才知道如何使用。
2、協議的支持
dubbo2.7使用默認的序列化協議是hessian,如果參數是泛型 ,返回值可能為null,此時可以修改序列化為 kryo,並加入:
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
</dependency>
3、版本的區別
目前dubbo 2.7 版本(包括2.7以下)建議使用dubbo作為RPC調用協議(也是默認)。
本人測試了一下,dubbo 3.0 版本情況下即使按照官方文檔,引入 hessian 協議的依賴,仍然報:
No such extension org.apache.dubbo.rpc.Protocol by name hessian, no related exception was found, please check whether related SPI module is missing.
暫時沒有找到解決方案。
使用dubbo 2.7.13 版本,是可以單獨使用hessian協議的。
除此之外本人從網上+測試驗證:
- dubbo3.0版本是2021年6月才出來的,新版本默認是使用 trilple 協議,還不兼容舊版本協議
- dubbo3.0官方並沒有詳細的使用文檔,本人測試了一下,與2.7的兼容性還有很大問題,所以建議還是使用2.7版本
- dubbo更新太慢(3年都沒有更新3.0版本,捐贈給Apache后基本無維護),網上找到的資料不如springcloud多
4、springboot配置文件
建議springboot接入dubbo不要使用 注解的方式 配置dubbo,統一使用xml文件更友好。
5、關於 dubbo-registry-zookeeper 和 dubbo-dependencies-zookeeper
測試了一下,兩個使用其中一個即可,推薦使用 dubbo-registry-zookeeper
參考: