文章目錄
-
-
- 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()
ref 是服務引用配置對象的成員屬性,代表這一個服務接口代理引用。
如果為空,那麼會執行init()方法建構一個ref,然後傳回。
ReferenceConfig.init()
該方法代碼很長,忽略掉和本文主題内容無關的部分,主要是會調用createProxy建立服務接口代理引用。
之前的部分是設定一些必要的參數,map參數内容如下
ReferenceConfig.createProxy(Map<String, String> map)
分兩種情況,如果服務的提供者就在同一個jvm,那麼直接使用本地服務引用
否則走遠端服務引用的邏輯。
遠端服務引用,首先更新urls。
根據urls的大小,如果隻有1個的話,那麼invoker就是調用Protocol.refer得到的結果。
如果是多個的話,将分别的調用Protocol.refer得到多個invoker。将多個invoker合并成一個cluster invoker。
重點應該放在如何得到invoker,也就是Protocol.refer做了什麼
QosProtocolWrapper.refer(Class< T > type, URL url)
首先來到QosProtocolWrapper類,從這裡開始會出現一系列的裝飾者+責任鍊模式的代碼。
QosProtocolWrapper,ProtocolFilterWrapper,ProtocolListenerWrapper,DubboProtocol這4個類都實作了Protocol接口,内部有屬性Protocol,比如:
QosProtocolWrapper.refer的代碼如下:
如果目前url是個注冊類型的url的話,那麼會開啟Qos服務,然後以責任鍊的方式,調用下一個Protocol的refer方法。Qos服務不是本文的重點,暫時掠過。
ProtocolFilterWrapper.refer(Class< T > type, URL url)
同樣也是判斷是否是注冊類型的url,如果是的話,那麼調用下一個Protocol的refer方法
否則調用完Protocol的refer方法之後,還需要将invokr封裝成一個invoker鍊條,添加上過濾器的功能。
ProtocolListenerWrapper.refer(Class< T > type, URL url)
和上面的類似,将invoker封裝成ListenerInvokerWrapper
AbstractProtocol.refer(Class< T > type, URL url)
基于協定建構服務引用,封裝成AsyncToSyncInvoker對象。
這裡使用的是Dubbo協定,是以會調用DubboProtocol子類實作的protocolBindingRefer方法
DubboProtocol.protocolBindingRefer(Class< T > serviceType, URL url)
建立一個rpc invoker,重點在于getClients方法,得到的clients在封裝成DubboInvokder。
然後添加早invokers集合當中,傳回DubboInvokder。
getClients方法如下:
從url中得到參數,連接配接數。
如果沒有配置連接配接數參數為0,預設使用的是共享連接配接,否則的話,每次都使用新的rpc連接配接。
共享連接配接通過getSharedClient獲得,否則使用initClient初始化一個用戶端。下面就來看看這兩個方法具體的差别。
getSharedClient
從url當中擷取ip位址+端口号。
基于ip位址+端口号 從referenceClientMap當中尋找,是否有緩存的用戶端服務引用。
有的話,那麼檢查這些服務引用是否可用。如果可用的話,修改引用數量,然後傳回即可。
如果不可用,或者緩存當中沒有,那麼就會繼續下面的代碼。
并發處理,雙重檢查,為了避免建構重複的引用。
如果緩存當中沒有,那麼基于連接配接數量建構服務引用。也就是走這一段代碼
如果不為空的話,那麼說明是部分用戶端服務引用關閉了,那麼就需要重新的建構服務引用。對應下面的代碼
兩種情況都差不多,都會調用buildReferenceCountExchangeClient(URL url)
DubboProtocol.buildReferenceCountExchangeClient(Url url)
這裡隻做了一層額外的封裝,下面看看初始化用戶端的邏輯。
DubboProtocol.initClient(URL url)
調用Exchangers.connect,下面又是一連串的connect責任鍊調用。
HeaderExchanger.connect(URL url, ExchangeHandler handler)
參數校驗
HeaderExchanger.connect(URL url, ExchangeHandler handler)
建構DecodeHandler,解碼處理器
Transporters.connect(URL url, ChannelHandler… handlers)
NettyTransporter.connect(URL url, ChannelHandler handler)
建構Netty用戶端
NettyClient構造函數
調用其父類的構造函數
doOpen()開啟netty用戶端
connect()使用netty用戶端連接配接遠端服務
以上就是服務引用的全過程。