天天看點

Dubbo 服務引用原了解析

文章目錄

      • 1.概述
      • 2.服務引用源碼分析

1.概述

上一篇部落格中介紹了dubbo服務暴露的原理,還不清楚的讀者可以先看上一篇部落格。部落格位址如下:

《Dubbo服務暴露原了解析,帶你手撕源碼》

本文将探究服務消費者如何引用服務,分析dubbo中服務引用的相關源碼。同樣的,為了聚焦在服務引用的過程,編寫如下的測試代碼,把關注點放在服務引用的過程上。

代碼如下:

// 用戶端
@Test
public void invokeRemote() {
    ReferenceConfig<UserService> referenceConfig = new ReferenceConfig();
    //  應用名稱
    ApplicationConfig app = new ApplicationConfig("client");
    referenceConfig.setApplication(app);
    RegistryConfig registryConfig = new RegistryConfig("zookeeper://127.0.0.1:2181");
    referenceConfig.setRegistry(registryConfig);
    // URL位址
    referenceConfig.setUrl("dubbo://127.0.0.1:8080/coderead.dubbo.test.dubbotest.UserService");
    // 指定接口
    referenceConfig.setInterface(UserService.class);
    // 擷取接口服務 (動态代理)
    UserService userService = referenceConfig.get();
  
    System.out.println(userService.getUser(1111));
}
           

上面的代碼模拟了一個服務消費者獲得服務引用接口并調用。

使用上一篇部落格中的代碼,開啟一個服務。

用戶端的測試代碼,首先構造一個服務引用配置對象,設定一些必要的服務引用需要的配置資訊,例如配置注冊中心,設定服務接口資訊等。

然後通過referenceConfig.get() 得到服務引用,也就是userService接口的代理對象。

最後通過服務引用代理對象調用getUser方法進行遠端調用。

2.服務引用源碼分析

ReferenceConfig.get()

Dubbo 服務引用原了解析

ref 是服務引用配置對象的成員屬性,代表這一個服務接口代理引用。

Dubbo 服務引用原了解析

如果為空,那麼會執行init()方法建構一個ref,然後傳回。

ReferenceConfig.init()

該方法代碼很長,忽略掉和本文主題内容無關的部分,主要是會調用createProxy建立服務接口代理引用。

之前的部分是設定一些必要的參數,map參數内容如下

Dubbo 服務引用原了解析

ReferenceConfig.createProxy(Map<String, String> map)

分兩種情況,如果服務的提供者就在同一個jvm,那麼直接使用本地服務引用

否則走遠端服務引用的邏輯。

遠端服務引用,首先更新urls。

根據urls的大小,如果隻有1個的話,那麼invoker就是調用Protocol.refer得到的結果。

如果是多個的話,将分别的調用Protocol.refer得到多個invoker。将多個invoker合并成一個cluster invoker。

重點應該放在如何得到invoker,也就是Protocol.refer做了什麼

Dubbo 服務引用原了解析
Dubbo 服務引用原了解析

QosProtocolWrapper.refer(Class< T > type, URL url)

首先來到QosProtocolWrapper類,從這裡開始會出現一系列的裝飾者+責任鍊模式的代碼。

Dubbo 服務引用原了解析

QosProtocolWrapper,ProtocolFilterWrapper,ProtocolListenerWrapper,DubboProtocol這4個類都實作了Protocol接口,内部有屬性Protocol,比如:

Dubbo 服務引用原了解析

QosProtocolWrapper.refer的代碼如下:

Dubbo 服務引用原了解析

如果目前url是個注冊類型的url的話,那麼會開啟Qos服務,然後以責任鍊的方式,調用下一個Protocol的refer方法。Qos服務不是本文的重點,暫時掠過。

ProtocolFilterWrapper.refer(Class< T > type, URL url)

同樣也是判斷是否是注冊類型的url,如果是的話,那麼調用下一個Protocol的refer方法

否則調用完Protocol的refer方法之後,還需要将invokr封裝成一個invoker鍊條,添加上過濾器的功能。

Dubbo 服務引用原了解析

ProtocolListenerWrapper.refer(Class< T > type, URL url)

和上面的類似,将invoker封裝成ListenerInvokerWrapper

Dubbo 服務引用原了解析

AbstractProtocol.refer(Class< T > type, URL url)

基于協定建構服務引用,封裝成AsyncToSyncInvoker對象。

Dubbo 服務引用原了解析

這裡使用的是Dubbo協定,是以會調用DubboProtocol子類實作的protocolBindingRefer方法

DubboProtocol.protocolBindingRefer(Class< T > serviceType, URL url)

Dubbo 服務引用原了解析

建立一個rpc invoker,重點在于getClients方法,得到的clients在封裝成DubboInvokder。

然後添加早invokers集合當中,傳回DubboInvokder。

getClients方法如下:

Dubbo 服務引用原了解析

從url中得到參數,連接配接數。

如果沒有配置連接配接數參數為0,預設使用的是共享連接配接,否則的話,每次都使用新的rpc連接配接。

共享連接配接通過getSharedClient獲得,否則使用initClient初始化一個用戶端。下面就來看看這兩個方法具體的差别。

getSharedClient

Dubbo 服務引用原了解析

從url當中擷取ip位址+端口号。

基于ip位址+端口号 從referenceClientMap當中尋找,是否有緩存的用戶端服務引用。

有的話,那麼檢查這些服務引用是否可用。如果可用的話,修改引用數量,然後傳回即可。

如果不可用,或者緩存當中沒有,那麼就會繼續下面的代碼。

并發處理,雙重檢查,為了避免建構重複的引用。

如果緩存當中沒有,那麼基于連接配接數量建構服務引用。也就是走這一段代碼

Dubbo 服務引用原了解析
Dubbo 服務引用原了解析

如果不為空的話,那麼說明是部分用戶端服務引用關閉了,那麼就需要重新的建構服務引用。對應下面的代碼

Dubbo 服務引用原了解析

兩種情況都差不多,都會調用buildReferenceCountExchangeClient(URL url)

DubboProtocol.buildReferenceCountExchangeClient(Url url)

Dubbo 服務引用原了解析

這裡隻做了一層額外的封裝,下面看看初始化用戶端的邏輯。

DubboProtocol.initClient(URL url)

Dubbo 服務引用原了解析

調用Exchangers.connect,下面又是一連串的connect責任鍊調用。

HeaderExchanger.connect(URL url, ExchangeHandler handler)

Dubbo 服務引用原了解析

參數校驗

HeaderExchanger.connect(URL url, ExchangeHandler handler)

建構DecodeHandler,解碼處理器

Dubbo 服務引用原了解析

Transporters.connect(URL url, ChannelHandler… handlers)

Dubbo 服務引用原了解析

NettyTransporter.connect(URL url, ChannelHandler handler)

Dubbo 服務引用原了解析

建構Netty用戶端

NettyClient構造函數

Dubbo 服務引用原了解析

調用其父類的構造函數

Dubbo 服務引用原了解析

doOpen()開啟netty用戶端

connect()使用netty用戶端連接配接遠端服務

Dubbo 服務引用原了解析
Dubbo 服務引用原了解析

以上就是服務引用的全過程。