1. 程式人生 > >(轉)Dubbo—Provider到Consumer實現細節

(轉)Dubbo—Provider到Consumer實現細節

概述

本文介紹以下內容:

  • 初始化過程細節
  • 遠端呼叫細節
  • 遠端通訊細節

初始化過程細節

解析服務

基於 dubbo.jar 內的 META-INF/spring.handlers 配置,Spring 在遇到 dubbo 名稱空間時,會回撥 DubboNamespaceHandler。

所有 dubbo 的標籤,都統一用 DubboBeanDefinitionParser 進行解析,基於一對一屬性對映,將 XML 標籤解析為 Bean 物件。

在 ServiceConfig.export() 或 ReferenceConfig.get() 初始化時,將 Bean 物件轉換 URL 格式,所有 Bean 屬性轉成 URL 的引數。

然後將 URL 傳給 協議擴充套件點,基於擴充套件點的 擴充套件點自適應機制,根據 URL 的協議頭,進行不同協議的服務暴露或引用。

暴露服務

  1. 只暴露服務埠:
    在沒有註冊中心,直接暴露提供者的情況下 [1],ServiceConfig 解析出的 URL 的格式為: dubbo://service-host/com.foo.FooService?version=1.0.0。

基於擴充套件點自適應機制,通過 URL 的 dubbo:// 協議頭識別,直接呼叫 DubboProtocol的 export() 方法,開啟服務埠。

  1. 向註冊中心暴露服務:
    在有註冊中心,需要註冊提供者地址的情況下 [2],ServiceConfig 解析出的 URL 的格式為: registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode(“dubbo://service-host/com.foo.FooService?version=1.0.0”),

基於擴充套件點自適應機制,通過 URL 的 registry:// 協議頭識別,就會呼叫 RegistryProtocol 的 export() 方法,將 export 引數中的提供者 URL,先註冊到註冊中心。

再重新傳給 Protocol 擴充套件點進行暴露: dubbo://service-host/com.foo.FooService?version=1.0.0,然後基於擴充套件點自適應機制,通過提供者 URL 的 dubbo:// 協議頭識別,就會呼叫 DubboProtocol 的 export() 方法,開啟服務埠。

引用服務

  1. 直連引用服務:
    在沒有註冊中心,直連提供者的情況下 [3],ReferenceConfig 解析出的 URL 的格式為:dubbo://service-host/com.foo.FooService?version=1.0.0。

基於擴充套件點自適應機制,通過 URL 的 dubbo:// 協議頭識別,直接呼叫 DubboProtocol 的 refer() 方法,返回提供者引用。

  1. 從註冊中心發現引用服務:
    在有註冊中心,通過註冊中心發現提供者地址的情況下 [4],ReferenceConfig 解析出的 URL 的格式為: registry://registry-host/com.alibaba.dubbo.registry.RegistryService?refer=URL.encode(“consumer://consumer-host/com.foo.FooService?version=1.0.0”)。

基於擴充套件點自適應機制,通過 URL 的 registry:// 協議頭識別,就會呼叫 RegistryProtocol 的 refer() 方法,基於 refer 引數中的條件,查詢提供者 URL,如: dubbo://service-host/com.foo.FooService?version=1.0.0。

基於擴充套件點自適應機制,通過提供者 URL 的 dubbo:// 協議頭識別,就會呼叫 DubboProtocol 的 refer() 方法,得到提供者引用。

然後 RegistryProtocol 將多個提供者引用,通過 Cluster 擴充套件點,偽裝成單個提供者引用返回。

攔截服務
基於擴充套件點自適應機制,所有的 Protocol 擴充套件點都會自動套上 Wrapper 類。

基於 ProtocolFilterWrapper 類,將所有 Filter 組裝成鏈,在鏈的最後一節呼叫真實的引用。

基於 ProtocolListenerWrapper 類,將所有 InvokerListener 和 ExporterListener 組裝集合,在暴露和引用前後,進行回撥。

包括監控在內,所有附加功能,全部通過 Filter 攔截實現。

遠端呼叫細節

服務提供者暴露一個服務的詳細過程

image

上圖是服務提供者暴露服務的主過程:

首先 ServiceConfig 類拿到對外提供服務的實際類 ref(如:HelloWorldImpl),然後通過 ProxyFactory 類的 getInvoker 方法使用 ref 生成一個 AbstractProxyInvoker 例項,到這一步就完成具體服務到 Invoker 的轉化。接下來就是 Invoker 轉換到 Exporter 的過程。

Dubbo 處理服務暴露的關鍵就在 Invoker 轉換到 Exporter 的過程,上圖中的紅色部分。下面我們以 Dubbo 和 RMI 這兩種典型協議的實現來進行說明:

Dubbo 的實現

Dubbo 協議的 Invoker 轉為 Exporter 發生在 DubboProtocol 類的 export 方法,它主要是開啟 socket 偵聽服務,並接收客戶端發來的各種請求,通訊細節由 Dubbo 自己實現。

RMI 的實現

RMI 協議的 Invoker 轉為 Exporter 發生在 RmiProtocol類的 export 方法,它通過 Spring 或 Dubbo 或 JDK 來實現 RMI 服務,通訊細節這一塊由 JDK 底層來實現,這就省了不少工作量。

服務消費者消費一個服務的詳細過程

image

上圖是服務消費的主過程:

首先 ReferenceConfig 類的 init 方法呼叫 Protocol 的 refer 方法生成 Invoker 例項(如上圖中的紅色部分),這是服務消費的關鍵。接下來把 Invoker 轉換為客戶端需要的介面(如:HelloWorld)。

關於每種協議如 RMI/Dubbo/Web service 等它們在呼叫 refer 方法生成 Invoker 例項的細節和上一章節所描述的類似。

滿眼都是 Invoker
由於 Invoker 是 Dubbo 領域模型中非常重要的一個概念,很多設計思路都是向它靠攏。這就使得 Invoker 滲透在整個實現程式碼裡,對於剛開始接觸 Dubbo 的人,確實容易給搞混了。 下面我們用一個精簡的圖來說明最重要的兩種 Invoker:服務提供 Invoker 和服務消費 Invoker:

/dev-guide/images/dubbo_rpc_invoke.jpg

為了更好的解釋上面這張圖,我們結合服務消費和提供者的程式碼示例來進行說明:

服務消費者程式碼:

public class DemoClientAction {
 
    private DemoService demoService;
 
    public void setDemoService(DemoService demoService) {
        this.demoService = demoService;
    }
 
    public void start() {
        String hello = demoService.sayHello("world" + i);
    }
}

上面程式碼中的 DemoService 就是上圖中服務消費端的 proxy,使用者程式碼通過這個 proxy 呼叫其對應的 Invoker [5],而該 Invoker 實現了真正的遠端服務呼叫。

服務提供者程式碼:

public class DemoServiceImpl implements DemoService {
 
    public String sayHello(String name) throws RemoteException {
        return "Hello " + name;
    }
}

上面這個類會被封裝成為一個 AbstractProxyInvoker 例項,並新生成一個 Exporter 例項。這樣當網路通訊層收到一個請求後,會找到對應的 Exporter 例項,並呼叫它所對應的 AbstractProxyInvoker 例項,從而真正呼叫了服務提供者的程式碼。Dubbo 裡還有一些其他的 Invoker 類,但上面兩種是最重要的。

遠端通訊細節

協議頭約定
image

執行緒派發模型
image

  • Dispather: all, direct, message, execution, connection
  • ThreadPool: fixed, cached

註解:

  1. 即:<dubbo:service regisrty=“N/A” /> 或者 <dubbo:registry address=“N/A” /> ↩︎
  2. 即: <dubbo:registry address=“zookeeper://10.20.153.10:2181” /> ↩︎
  3. 即:<dubbo:reference url=“dubbo://service-host/com.foo.FooService?version=1.0.0” /> ↩︎
  4. 即:<dubbo:registry address=“zookeeper://10.20.153.10:2181” />
  5. ↩︎DubboInvoker、 HessianRpcInvoker、 InjvmInvoker、 RmiInvoker、 WebServiceInvoker 中的任何一個