目錄
一、dubbo簡介
1.1 dubbo是什麼
dubbo是一款高性能、輕量級的開源Java RPC框架
1.2 dubbo主要功能
-
面向接口的遠程方法調用
-
智能容錯和負載均衡
-
以及服務自動註冊和發現
1.3 dubbo結構
角色:註冊中心,服務提供者,服務消費者,dubbo框架容器,監控中心
dubbo啓動流程:
0)dubbo容器啓動,加載、運行服務提供者
1)服務提供者啓動時,向註冊中心註冊自己的服務
2)服務消費者啓動時,從註冊中心訂閱自己所需的服務
3)註冊中心返回服務提供者的地址列表給消費者,當服務提供者發生變化時;註冊中心將基於長連接推送變更數據給消費者
4)服務消費者基於軟負載均衡策略執行服務調用
5)服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心
1.4 dubbo發展歷程
獨家專訪阿里高級技術專家北緯:Dubbo開源重啓半年來的快意江湖
二、dubbo環境搭建&快速啓動
2.1 註冊中心(zookeeper)安裝
wget http://mirrors.cnnic.cn/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz #嫌慢的化可以直接下載附件
tar -zxvf zookeeper-3.4.14.tar.gz
cd zookeeper-3.4.14
cp conf/zoo_sample.cfg conf/zoo.cfg #解壓包裏面只有zoo_sample.cfg,而沒有zoo.cfg,zookeeper的啓動依賴zoo.cfg
#按照自己需求配置zookeeper啓動屬性
bash zkServer.sh start #默認啓動端口爲2181
註冊中心 |
成熟度 |
優點 |
缺點 |
建議 |
Zookeeper註冊中心 |
Stable |
支持基於網絡的集羣方式,有廣泛周邊開源產品,建議使用dubbo-2.3.3以上版本(推薦使用) |
依賴於Zookeeper的穩定性 |
可用於生產環境 |
Redis註冊中心 |
Stable |
支持基於客戶端雙寫的集羣方式,性能高 |
要求服務器時間同步,用於檢查心跳過期髒數據 |
可用於生產環境 |
Multicast註冊中心 |
Tested |
去中心化,不需要安裝註冊中心 |
依賴於網絡拓撲和路由,跨機房有風險 |
小規模應用或開發測試環境 |
Simple註冊中心 |
Tested |
Dogfooding,註冊中心本身也是一個標準的RPC服務 |
沒有集羣支持,可能單點故障 |
試用 |
2.2 dubbo管理控制檯安裝
SpringBoot + dubbo-admin-server + dubbo-admin-ui:(可選,不影響使用。安裝管理控制檯,可以可視化的管理服務)
-
下載管理控制檯 https://github.com/apache/incubator-dubbo-admin (springboot項目)
-
啓動dubbo-admin-server(後面需要在本地啓動服務提供中和服務消費者,爲了防止端口衝突,此處配置端口爲8888)
-
啓動dubbo-admin-ui,前端使用的是vue,需要Node環境
nvm install 8.4.0 nvm use 8.4.0 cd dubbo-admin-ui npm install #更改config/index.js的target端口號爲8888(dubbo-admin-server配置的端口號) npm run dev #瀏覽器訪問localhost:8081
2.3 dubbo服務提供者
-
pom.xml
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo-bom</artifactId> <version>2.6.4</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.4</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.10.RELEASE</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> </dependencies>
-
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"></dubbo:application> <!--指定註冊中心位置--> <dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry> <!--指定通信規則--> <dubbo:protocol name="dubbo" port="20882"></dubbo:protocol> <!--暴露服務:interface指定接口名,ref指向實現類--> <dubbo:service interface="com.sankuai.api.HelloWorldService" ref="helloWorldService"></dubbo:service> <!--服務實現--> <bean id="helloWorldService" class="com.sankuai.impl.HelloWorldServiceImpl" ></bean> </beans>
-
provider代碼
3.1 HelloWorldService.java
public interface HelloWorldService { String sayHello(String name); }
3.2 HelloWorldServiceImpl.java
import com.sankuai.api.HelloWorldService; import org.springframework.stereotype.Service; @Service("helloWorldService") public class HelloWorldServiceImpl implements HelloWorldService { @Override public String sayHello(String name) { System.out.println("hello" + name); return "hello" + name; } }
3.3 加載spring配置
import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("provider.xml"); ctx.start(); System.in.read(); } }
3.4 在監控中心觀察到服務
2.4 dubbo消費者
-
pom.xml
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo-bom</artifactId> <version>2.6.4</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.4</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.10.RELEASE</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>com.sankuai</groupId> <artifactId>dubbo.studying</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
-
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" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.sankuai.consumer.impl"></context:component-scan> <!--消費方appkey --> <dubbo:application name="consumer-of-helloworld-app" /> <!-- 註冊中心地址 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 指定需要使用的服務 --> <dubbo:reference id="helloWorldService" interface="com.sankuai.api.HelloWorldService" /> </beans>
-
consumer代碼
3.1 Conmunicate.java
public interface Conmunicate { String communicate(String name) ; }
3.2 ConmunicateImpl.java
import com.sankuai.api.HelloWorldService; import com.sankuai.consumer.api.Conmunicate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("conmunicateService") public class ConmunicateImpl implements Conmunicate { @Autowired HelloWorldService helloWorldService; public String communicate(String name) { return helloWorldService.sayHello(name); } }
3.3 加載spring配置,並調用遠程服務
import com.sankuai.consumer.impl.ConmunicateImpl; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("consumer.xml"); ConmunicateImpl conmunicate = (ConmunicateImpl) ctx.getBean("conmunicateService"); String s = conmunicate.communicate("zhuyafeng"); System.out.println(s); System.in.read(); } }
3.4 監控中心觀察消費者信息
2.5 dubbo監控中心安裝
三、dubbo配置
3.1 配置之間的依賴關係
3.2 dubbo標籤配置
標籤 |
用途 |
解釋 |
備註 |
---|---|---|---|
<dubbo:service/> |
服務配置 |
用於暴露一個服務,定義服務的元信息,一個服務可以用多個協議暴露,一個服務也可以註冊到多個註冊中心 |
|
<dubbo:reference/> |
引用配置 |
用於創建一個遠程服務代理,一個引用可以指向多個註冊中心 |
引用缺省是延遲初始化的,只有引用被注入到其它 Bean,或被 getBean() 獲取,纔會初始化。如果需要飢餓加載,即沒有人引用也立即生成動態代理,可以配置:<dubbo:reference ... init="true" /> |
<dubbo:protocol/> |
協議配置 |
用於配置提供服務的協議信息,協議由提供方指定,消費方被動接受 |
|
<dubbo:application/> |
應用配置 |
用於配置當前應用信息,不管該應用是提供者還是消費者 |
|
<dubbo:module/> |
模塊配置 |
用於配置當前模塊信息,可選 |
|
<dubbo:registry/> |
註冊中心配置 |
用於配置連接註冊中心相關信息 |
|
<dubbo:monitor/> |
監控中心配置 |
用於配置連接監控中心相關信息,可選 |
|
<dubbo:provider/> |
提供方配置 |
當 ProtocolConfig 和 ServiceConfig 某屬性沒有配置時,採用此缺省值,可選 |
|
<dubbo:consumer/> |
消費方配置 |
當 ReferenceConfig 某屬性沒有配置時,採用此缺省值,可選 |
|
<dubbo:method/> |
方法配置 |
用於 ServiceConfig 和 ReferenceConfig 指定方法級的配置信息 |
|
<dubbo:argument/> |
參數配置 |
用於指定方法參數配置 |
|
3.3 常用dubbo配置(以xml配置爲例)
功能 |
功能 |
配置方式 |
備註 |
---|---|---|---|
啓動時檢查 |
Dubbo 缺省會在啓動時檢查依賴的服務是否可用,不可用時會拋出異常,阻止 Spring 初始化完成,以便上線時,能及早發現問題,默認 check="true" |
<dubbo:reference check="false> 關閉某個服務檢查 <dubbo:consumer check="false> 關閉所有服務檢查 <dubbo:register check="false"> 關閉註冊中心檢查 |
|
集羣容錯 |
在集羣調用失敗時,Dubbo 提供了多種容錯方案,缺省爲 failover 重試 |
集羣容錯模式 <dubbo:service cluster="failover" /> failover Cluster:失敗自動切換,當出現失敗,重試其它服務器 Failfast Cluster:快速失敗,只發起一次調用,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄。 Failsafe Cluster:失敗安全,出現異常時,直接忽略。通常用於寫入審計日誌等操作。 Failback Cluster:失敗自動恢復,後臺記錄失敗請求,定時重發。通常用於消息通知操作 Forking Cluster:並行調用多個服務器,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。
|
默認爲failover,配合重試使用<dubbo:service retries="2" />(支持服務級,reference級和方法級配置) |
負載均衡 |
在集羣負載均衡時,Dubbo 提供了多種均衡策略,缺省爲 random 隨機調用 |
服務端服務級別 <dubbo:service interface="..." loadbalance="roundrobin" /> 客戶端服務級別 <dubbo:reference interface="..." loadbalance="roundrobin" /> 服務端方法級別 <dubbo:service interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:service> 客戶端方法級別 <dubbo:reference interface="..."> <dubbo:method name="..." loadbalance="roundrobin"/> </dubbo:reference> |
|
直連服務提供者 |
在開發及測試環境下,經常需要繞過註冊中心,只測試指定服務提供者,這時候可能需要點對點直連,點對點直連方式,將以服務接口爲單位,忽略註冊中心的提供者列表,A 接口配置點對點,不影響 B 接口從註冊中心獲取列表。 |
<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" /> |
不要在線上使用!!! |
多協議暴露 |
Dubbo 允許配置多協議,在不同服務上支持不同協議或者同一服務上同時支持多種協議。 |
|
|
服務分組 |
當一個接口有多種實現時,可以用 group 區分 |
服務提供者
服務消費者
|
dubbo2.2.0以上版本,服務消費者支持使用任意版本
|
多版本 |
當一個接口實現,出現不兼容升級時,可以用版本號過渡,版本號不同的服務相互間不引用。 可以按照以下的步驟進行版本遷移:
|
老版本服務提供者配置: <dubbo:service interface="com.foo.BarService" version="1.0.0" /> 新版本服務提供者配置: <dubbo:service interface="com.foo.BarService" version="2.0.0" /> 老版本服務消費者配置: <dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" /> 新版本服務消費者配置: <dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" /> 如果不需要區分版本,可以按照以下的方式配置 [1]: <dubbo:reference id="barService" interface="com.foo.BarService" version="*" /> |
Consumer只能消費與自己 “接口+分組+版本號” 一致的Provider提供的服務。
|
結果緩存 |
結果緩存 ,用於加速熱門數據的訪問速度,Dubbo 提供聲明式緩存,以減少用戶加緩存的工作量 |
接口級設置
方法級設置
|
|
上下文信息 |
獲取當前RPC的上下文信息 |
|
|
provider異步執行 |
|
1.定義CompletableFuture簽名的接口
通過return CompletableFuture.supplyAsync(),業務執行已從Dubbo線程切換到業務線程,避免了對Dubbo線程池的阻塞。 2.使用AsyncContext Dubbo提供了一個類似Serverlet 3.0的異步接口AsyncContext,在沒有CompletableFuture簽名接口的情況下,也可以實現Provider端的異步執行。
|
|
consumer異步調用 |
|
1.使用CompletableFuture簽名的接口,此方法依賴於服務提供者事先定義CompletableFuture簽名的服務
2.使用RpcContext
3. 利用Java 8提供的default接口實現,重載一個帶有帶有CompletableFuture簽名的方法
|
方法1是直接使用服務端實現的異步執行
方法2,3是在客戶端執行異步調用
Provider端異步執行和Consumer端異步調用是相互獨立的,你可以任意正交組合兩端配置
|
本地僞裝 |
本地僞裝通常用於服務降級,比如某驗權服務,當服務提供方全部掛掉後,客戶端不拋出異常,而是通過 Mock 數據返回授權失敗。 |
|
|
本地僞裝 |
本地僞裝通常用於服務降級,比如某驗權服務,當服務提供方全部掛掉後,客戶端不拋出異常,而是通過 Mock 數據返回授權失敗。 |
|
|
令牌驗證 |
通過令牌驗證在註冊中心控制權限,以決定要不要下發令牌給消費者,可以防止消費者繞過註冊中心訪問提供者,另外通過註冊中心可靈活改變授權方式,而不需修改或升級提供者 |
可以全局設置開啓令牌驗證: <!--隨機token令牌,使用UUID生成--> <dubbo:provider interface="com.foo.BarService" token="true" /> 或 <!--固定token令牌,相當於密碼--> <dubbo:provider interface="com.foo.BarService" token="123456" /> 也可在服務級別設置: <!--隨機token令牌,使用UUID生成--> <dubbo:service interface="com.foo.BarService" token="true" /> 或 <!--固定token令牌,相當於密碼--> <dubbo:service interface="com.foo.BarService" token="123456" /> |
|
線程棧自動dump |
當業務線程池滿時,我們需要知道線程都在等待哪些資源、條件,以找到系統的瓶頸點或異常點。dubbo通過Jstack自動導出線程堆棧來保留現場,方便排查問題
|
默認策略:
指定導出路徑: # dubbo.properties dubbo.application.dump.directory=/tmp <dubbo:application ...> <dubbo:parameter key="dump.directory" value="/tmp" /> </dubbo:application>
|
|
序列化 |
dubbo支持多種序列化方式,支持使用高效的Java序列化(Kryo和FST) |
<dubbo:protocol name="dubbo" serialization="kryo"/> <dubbo:protocol name="dubbo" serialization="fst"/> |
性能:dubbo>hession2>json>java 其中dubbo序列化爲阿里自研,不建議生產環境使用 |
3.3 dubbo配置屬性的覆蓋策略
dubbo的屬性配置可以出現在狠多地方,關於這些配置的查找順序符合以下規則:
-
方法級優先,接口級次之,全局配置再次之。
-
如果級別一樣,則消費方優先,提供方次之。
四、Dubbo執行過程
4.1 服務註冊時序圖
4.2 服務發現時序圖
4.3 服務調用
五、橫向對比
5.1 各大互聯網公司使用的RPC框架
公司 |
RPC框架 |
---|---|
阿里巴巴 |
HSF、Dubbo |
騰訊 |
|
百度 |
|
美團點評 |
mtthrift(基於thrift)、pigeon |
微博 |
|
京東 |
JSF(基於Dubbo) |
網易考拉 |
Dubbok(基於Dubbo) |
噹噹 |
Dubbox(基於Dubbo) |
|
|
|
|
|
5.2 pigeon&dubbo對比
|
Dubbo |
pigeon |
備註 |
---|---|---|---|
開發語言 |
Java |
Java |
|
分佈式(服務治理) |
dubbo monitor |
pigeon |
|
序列化方式 |
dubbo(基於hession2)、hession、json、jdk fst、kryo 支持擴展 |
hessian、json、protobuf3、thrift、jdk fst 支持擴展 |
|
註冊中心 |
zookeeper、redis、multicast、simple |
zookeeper->mns(基於zookeeper) |
|
跨編程語言 |
不支持 |
不支持 |
|
配置方式 |
註解配置、schema配置、api配置 |
註解配置、schema配置、屬性配置、api配置 |
|
服務通信協議 |
Dubbo 協議、 Rmi 協議、 Hessian 協議、 HTTP 協議、 WebService 協議、Dubbo Thrift 協議、Memcached 協議 |
HTTP協議,TCP協議 |
|
負載均衡 |
RandomLoadBalance (默認)、RoundRobinLoadBalance 、 ConsistentHash、LeastActive |
WeightedAutoaware(默認,根據客戶端到服務節點的在途請求數,結合有效權重,重新計算請求容量,挑選請求容量最小的服務節點進行隨機負載)、Random、RoundRobin |
|
服務容錯 |
Failover(默認策略,重試次數2) Failfast(只發起一次調用,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄) Failsafe(失敗安全,出現異常時,直接忽略。通常用於寫入審計日誌等操作) Failback (失敗自動恢復,後臺記錄失敗請求,定時重發。通常用於消息通知操作) Forking(調用多個服務器,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。可通過 forks="2" 來設置最大並行數) Broadcast(廣播調用所有提供者,逐個調用,任意一臺報錯則報錯 。通常用於通知所有提供者更新緩存或日誌等本地資源信息) |
failfast(默認,失敗之後直接返回) failover(失敗轉移策略,當調用一個節點失敗後,會調用另一個服務節點,重試次數默認爲-1,不顯示設置retry則不會重試) failsafe(調用失敗後,不拋異常,會返回默認值null) forking(並行策略,並行同時調用多個服務節點,以最先返回的結果爲最終結果返) hedged(發起調用後,超過hedgedDelay時間後未返回結果,會再次向其他服務節點發送一個請求,以最先返回的結果爲結果返回,主要用在解決服務調用長尾問題)
|
|
服務調用方式 |
同步(默認,通過配置asyn屬性) 異步 oneway(通過方法裏面配置return=“false”屬性,同時可以使用sent="true"來等待消息發出,發送失敗則拋異常) callback
事件通知:Consumer 端在調用之前、調用之後或出現異常時,觸發 oninvoke、onreturn、onthrow 三個事件。
|
sync 默認 oneway 提交請求,無需等待,不需要返回結果 future 請求提交給pigeon後立即返回,不等待返回結果,由pigeon負責等待返回結果,客戶端可以自行決定何時何地來取返回結果 callback 回調方式,客戶端將請求提交給pigeon後立即返回,也不等待返回結果,它與future方式的區別是,callback必須提供一個實現了pigeon提供的ServiceCallback接口的回調對象給pigeon,pigeon負責接收返回結果並傳遞迴給這個回調對象 |
其實不管是dubbo還是pigeon,同步調用的底層IO也是異步實現的。客戶端發起同步調用之後,會得到一個future對象,只不過同步調用的線程會阻塞到timeout,如果超時沒有還沒有返回,則返回調用失敗
調用方式判斷:DubboInvoker<T> |