dubbo源碼分析 之 服務遠程暴露(上)

因為 dubbo 遠程暴露裡面的過程還是比較複雜的,所以我就分為三個文章來講解 dubbo 的遠程暴露:

  1. dubbo 遠程暴露 -- Netty 暴露服務
  2. dubbo 遠程暴露 -- Zookeeper 連接
  3. dubbo 遠程暴露 -- Zookeeper 註冊 & 訂閱

這就篇就是分析 dubbo 服務暴露中通過 Netty 來暴露服務(當然 dubbo 還可以通過 Mina、Grizzly 來暴露服務,默認使用 Netty)。

1、ServiceConfig#doExportUrls

首先通過方法loadRegistries(true)來加載註冊中心。在方法checkRegistry()方法中判斷如果 xml 裡面沒有配置註解中心,從 dubbo 的 properties 文件中獲取(默認是dubbo.properties)。然後會返回List 作為配置信息的統一格式,所有擴展點都通過傳遞 URL 攜帶配置信息。URL的格式如下:

registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0 ...

因為 dubbo 支持多種協議,遍歷所有協議分別根據不同的協議把服務export到不同的註冊中心上去。

  1. 把配置的信息通過appendParameters提取到 map 中
  2. 判斷是否支持泛化調用
  3. 通過協議名稱、host、port、contextPath 和第一步提取出來的 map 構造協議的統一數據模型 URL (如:dubbo://169.254.69.197:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider ...)
  4. 循環遍註冊中心,把服務暴露在不同的註冊中心當中 a) 如果配置了 monitor,就返回監控統一模型數據 URL,並給以 monitor為 key 添加到生成的 URL中,URL格式如下:registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?export=dubbo%3A%2F%2F169.254.69.197%3A20880%2Fcom.alibaba.dubbo.demo.DemoService& ...

b) 把協議統一模型 URL 以export為 key,添加到註冊中心的統一模型 URL中

c) 根據服務的具體實現、實現的接口以及註冊中心統一模型 URL從代理工廠 ProxyFactory(SPI 默認獲取到 JavassistProxyFactory)獲取 Invoker對象。

dubbo源碼分析 之 服務遠程暴露(上)

d) 通過 Protocol#export(invoker) 暴露服務,因為註冊的協議是 registry 所以生成的 Protocol 對象如下圖所示。因為 ProtocolFilterWrapper和ProtocolFilterWrapper是過濾 registry協議的,所以最終通過 RegistryProtocol來處理暴露過程。

dubbo源碼分析 之 服務遠程暴露(上)

2、RegistryProtocol#export

根據這個類名我們就可以推測出這個類具有的功能,具有 Registry(註冊)與 Protocol (協議--服務暴露)在這個方法裡面就包括上面提到的三個邏輯:

  • dubbo 遠程暴露 -- Netty 暴露服務,通過配置的協議根據 SPI 獲取到對應的 Protocol對象,這裡是 DubboProtocol,對象。
  • dubbo 遠程暴露 -- Zookeeper 連接 服務註冊,通過RegistryFactory根據 SPI 獲取對應的 Registry 對象(ZookeeperRegistry),然後註冊到註冊中心上面去,供 consumer調用
  • dubbo 遠程暴露 -- Zookeeper 註冊 & 訂閱,它會把創建2個節點:一個是/dubbo/服務全類名/provider/...節點提供給服務消費方查看節點信息;二是/dubbo/服務全類名/configurators/...節點提供給服務方 watch(監控) dubbo-admin 對於服務的修改。比如:服務權重。

上面粗略的講了一下服務遠程暴露主要乾了哪些事,主要是想讓大家有一個全局的意識。下面我們就來講一下 dubbo 服務是如何通過 Netty 來暴露服務。

  1. getCacheKey(originInvoker),通過 Invoker 對象獲取到緩存 key,還記得我們在ServiceConfig#doExportUrls的 4-b 步驟裡面嗎?它就是把保存在 註冊統一模型裡面的 export key 獲取到協議的統一模型dubbo://169.254.69.197:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider ...,然後再刪除 dynamic與enabled 參數
  2. 從Map> bounds緩存中根據上面獲取的 key 獲取 Exporter 對象,如果獲取到直接返回;否則進行服務暴露
  3. 通過 originInvoker獲取裡面的 URL 獲取到協議的統一模型以及originInvoker本身創建 InvokerDelegete。
  4. 根據InvokerDelegete暴露服務,因為 URL 協議是 dubbo,所以獲取到的實例是 DubboProtocol,而這個對象因為協議不是 registry,所以生成ProtocolListenerWrapper會根據 SPI 機制檢測 dubbo 裡面配置的 InvokerListener 擴展;而 ProtocolFilterWrapper 會根據 SPI機制檢測 dubbo裡面配置的 Filter 擴展。所以最終通過 DubboProtocol來處理暴露過程。
dubbo源碼分析 之 服務遠程暴露(上)

  1. 暴露生成的 Exporter 和 傳入的 originInvoker 會創建 ExporterChangeableWrapper對象會以步驟 1 生成的 key 緩存在 Map> bounds 當中,並返回結果。

3、DubboProtocol#export

整個DubboProtocol#export的代碼如下:

public Exporter export(Invoker invoker) throws RpcException {

URL url = invoker.getUrl();

// export service.

String key = serviceKey(url);

DubboExporter exporter = new DubboExporter(invoker, key, exporterMap);

exporterMap.put(key, exporter);

//export an stub service for dispaching event

Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);

Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);

if (isStubSupportEvent && !isCallbackservice) {

String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);

if (stubServiceMethods == null || stubServiceMethods.length() == 0) {

if (logger.isWarnEnabled()) {

logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +

"], has set stubproxy support event ,but no stub methods founded."));

}

} else {

stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);

}

}

openServer(url);

return exporter;

這斷代碼主要的操作是:

  1. 根據傳入的Invoker中的 URL 通過serviceKey(url)獲取到 serviceKey,它的格式為:com.alibaba.dubbo.demo.DemoService:20880.
  2. 以傳的Invoker、第 1 步生成的 key 和 Map> exporterMap 生成 DubboExporter,並以第 1 步生成的 key 為索引,把生成的 DubboExporter添加到Map> exporterMap中
  3. 根據 URL 判斷是不是服務端,如果是服務端並且從Map serverMap獲取到的 ExchangeServer 為空,就通過DubboProtocol#createServer 創建服務,達到服務暴露的目的。返回DubboExporter對象

4、DubboProtocol#createServer

dubbo 遠程服務(

Provider)暴露最終其實就是創建一個 Netty Serve 服務,然後在 dubbo 在服務引用的時候創建一個 Netty Client 服務。其實 dubbo 遠程通信的原理其實就是基於 Socket 的遠程通信。下面我們來看一下 dubbo 是如何創建一個 Netty 服務的,下面就是它創建的序列圖:

dubbo源碼分析 之 服務遠程暴露(上)

它通過傳入 URL 與 requestHandler來創建一個 ExchangeServer,通過Netty 基於 NIO的形式通過自定義Channel來接收服務引用方傳遞過來的信息,以及發送調用遠程服務的本地方法後的數據給服務調用者。URL 裡面主要包含 IP 地址 與 端口信息用於創建 Socket 連接,而 requestHandler是一個 ExchangeHandler 通過自定義協議來處理 dubbo 的遠程通信。


分享到:


相關文章: