天天看點

Dubbo實作方案總覽

引言

從這篇文章開始,我們就開始深入到 Dubbo 的實作細節中,本文旨在一個較高的層面上總覽 Dubbo 的實作方案,其他 Dubbo 相關文章均收錄于

<Dubbo系列文章>

總覽

首先,我們簡單的介紹一下各個功能點的實作,然後再深入代碼中,詳細分析每個功能點的細節。

解析服務

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

所有 dubbo 的标簽,都統一用 DubboBeanDefinitionParser 進行解析,基于一對一屬性映射,将 XML 标簽解析為 Bean 對象。

在 ServiceConfig.export() 或 ReferenceConfig.get() 初始化時,将 Bean 對象轉換 URL 格式,所有 Bean 屬性轉成 URL 的參數。

然後将 URL 傳給 協定擴充點,基于擴充點的 擴充點自适應機制,根據 URL 的協定頭,進行不同協定的服務暴露或引用。

暴露服務

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

基于擴充點自适應機制,通過 URL 的 dubbo:// 協定頭識别,直接調用 DubboProtocol的 export() 方法,打開服務端口。

在有注冊中心,需要注冊提供者位址的情況下,ServiceConfig 解析出的 URL 的格式為: registry://registry-host/org.apache.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() 方法,打開服務端口。

Dubbo實作方案總覽

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

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

Dubbo 處理服務暴露的關鍵就在 Invoker 轉換到 Exporter 的過程,上圖中的紅色部分。

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

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

引用服務

在沒有注冊中心,直連提供者的情況下,ReferenceConfig 解析出的 URL 的格式為:dubbo://service-host/com.foo.FooService?version=1.0.0。

基于擴充點自适應機制,通過 URL 的 dubbo:// 協定頭識别,直接調用 DubboProtocol 的 refer() 方法,傳回提供者引用。

在有注冊中心,通過注冊中心發現提供者位址的情況下,ReferenceConfig 解析出的 URL 的格式為: registry://registry-host/org.apache.dubbo.registry.RegistryService?refer=URL.encode("dubbo://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 攔截實作。

消費服務

Dubbo實作方案總覽

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

首先 ReferenceConfig 類的 init 方法調用 Protocol 的 refer 方法生成 Invoker 執行個體(如上圖中的紅色部分),這是服務消費的關鍵。接下來把 Invoker 轉換為用戶端需要的接口(如:HelloWorld)。

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

Dubbo實作方案總覽

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

服務消費者代碼:

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,而該 Invoker 實作了真正的遠端服務調用。

服務提供者代碼:

public class DemoServiceImpl implements DemoService {

    public String sayHello(String name) throws RemoteException {
        return "Hello " + name;
    }
}           

上面這個類會被封裝成為一個 AbstractProxyInvoker 執行個體,并新生成一個 Exporter 執行個體。這樣當網絡通訊層收到一個請求後,會找到對應的 Exporter 執行個體,并調用它所對應的 AbstractProxyInvoker 執行個體,進而真正調用了服務提供者的代碼。Dubbo 裡還有一些其他的 Invoker 類,但上面兩種是最重要的。

Dubbo協定

Dubbo實作方案總覽
  • Magic - Magic High & Magic Low (16 bits)
  • Identifies dubbo protocol with value: 0xdabb
  • Req/Res (1 bit)

    Identifies this is a request or response. Request - 1; Response - 0.

  • 2 Way (1 bit)

    Only useful when Req/Res is 1 (Request), expect for a return value from server or not. Set to 1 if need a return value from server.

  • Event (1 bit)

    Identifies an event message or not, for example, heartbeat event. Set to 1 if this is an event.

  • Serialization ID (5 bit)
  • Identifies serialization type: the value for fastjson is 6.
  • Status (8 bits)

    Only useful when Req/Res is 0 (Response), identifies the status of response

    20 - OK

    30 - CLIENT_TIMEOUT

    31 - SERVER_TIMEOUT

    40 - BAD_REQUEST

    50 - BAD_RESPONSE

    60 - SERVICE_NOT_FOUND

    70 - SERVICE_ERROR

    80 - SERVER_ERROR

    90 - CLIENT_ERROR

    100 - SERVER_THREADPOOL_EXHAUSTED_ERROR

  • Request ID (64 bits)

    Identifies an unique request. Numeric (long).

  • Data Length (32)

    Length of the content (the variable part) after serialization, counted by bytes. Numeric (integer).

  • Variable Part

    Dubbo version、Service name、Service version、Method name、Method parameter types、Method arguments、Attachments、Return value type、Return value

線程模型

Dubbo實作方案總覽
  • Dispather: all, direct, message, execution, connection
  • ThreadPool: fixed, cached

文章說明

更多有價值的文章均收錄于

貝貝貓的文章目錄
Dubbo實作方案總覽

版權聲明: 本部落格所有文章除特别聲明外,均采用 BY-NC-SA 許可協定。轉載請注明出處!

創作聲明: 本文基于下列所有參考内容進行創作,其中可能涉及複制、修改或者轉換,圖檔均來自網絡,如有侵權請聯系我,我會第一時間進行删除。

參考内容

[1]《深入了解Apache Dubbo與實戰》

[2]

dubbo 官方文檔