(轉)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],ServiceConfig 解析出的 URL 的格式為: dubbo://service-host/com.foo.FooService?version=1.0.0。
基於擴充套件點自適應機制,通過 URL 的 dubbo:// 協議頭識別,直接呼叫 DubboProtocol的 export() 方法,開啟服務埠。
- 向註冊中心暴露服務:
在有註冊中心,需要註冊提供者地址的情況下 [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() 方法,開啟服務埠。
引用服務
- 直連引用服務:
在沒有註冊中心,直連提供者的情況下 [3],ReferenceConfig 解析出的 URL 的格式為:dubbo://service-host/com.foo.FooService?version=1.0.0。
基於擴充套件點自適應機制,通過 URL 的 dubbo:// 協議頭識別,直接呼叫 DubboProtocol 的 refer() 方法,返回提供者引用。
- 從註冊中心發現引用服務:
在有註冊中心,通過註冊中心發現提供者地址的情況下 [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 攔截實現。
遠端呼叫細節
服務提供者暴露一個服務的詳細過程
上圖是服務提供者暴露服務的主過程:
首先 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 底層來實現,這就省了不少工作量。
服務消費者消費一個服務的詳細過程
上圖是服務消費的主過程:
首先 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 類,但上面兩種是最重要的。
遠端通訊細節
協議頭約定
執行緒派發模型
- Dispather: all, direct, message, execution, connection
- ThreadPool: fixed, cached
註解:
- 即:<dubbo:service regisrty=“N/A” /> 或者 <dubbo:registry address=“N/A” /> ↩︎
- 即: <dubbo:registry address=“zookeeper://10.20.153.10:2181” /> ↩︎
- 即:<dubbo:reference url=“dubbo://service-host/com.foo.FooService?version=1.0.0” /> ↩︎
- 即:<dubbo:registry address=“zookeeper://10.20.153.10:2181” />
- ↩︎DubboInvoker、 HessianRpcInvoker、 InjvmInvoker、 RmiInvoker、 WebServiceInvoker 中的任何一個