原文地址:http://www.carlzone.cn/dubbo/11-dubbo-consuer-ref/
在使用 dubbo 的時候,我們對於遠程服務調用是無感知的。當需要調用遠程服務的時候我們只需要進行以下配置,就可以像本地調用的方式調用遠程服務:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="demo-consumer"/>
<dubbo:registry address="zookeeper://localhost:2181"/>
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService"/>
</beans>
下面的圖片是 dubbo 官方服務消費者消費一個服務的詳細過程的圖:
dubbo 的服務引用其實就是把上面的配置解析成 dubbo 框架的 ReferenceConfig(接口引用配置類)
,然後調用ReferenceConfig
類的 init
方法調用 Protocol
的 refer
方法生成 Invoker
實例(如上圖中的紅色部分),這是服務消費的關鍵。接下來把Invoker
轉換爲客戶端需要的接口(如:HelloService)。
關於每種協議如 RMI/Dubbo/Web service
等它們在調用 refer 方法生成 Invoker 實例。最後調用 ProxyFactory#getProxy(Invoker)
生成遠程暴露服務接口的代理對象。
那麼 dubbo 在框架內部是如何實現的呢?下面我們來具體分析一下它的源碼實現。
我們在 xml 裏面配置 dubbo 服務的引用,其實是 dubbo 實現了自定義命名空間的方式來集成 Spring 框架。這樣通過 Spring 的 IOC 特性可以很方便的創建 dubbo 的配置對象。
對於 <reference: />
標籤 Spring 會把它解析成 ReferenceBean 對象,並且這個對象是一個 FactoryBean。所以當我們通過 Spring 依賴注入這個對象的時候會調用 ReferenceBean#getObject
獲取接口對象的實例。它會把參數解析到一個 Map 中,然後根據其中的參數創建服務引用的代理對象。Map 裏面的值大概如下所示:
1、ReferenceConfig#createProxy
1、loadRegistries()
加載註冊中心配置,因爲 dubbo 支持多配置中心,所以返回 URL 的集合。 2、便利註冊中心 List<URL>
集合:加載監控中心 URL,如果配置了監控中心就在註冊 URL 加上monitor
;把服務引用的配置參數添加到註冊 URL 的 refer
參數上。 3、Protocol#refer
引用遠程服務,通過註冊中心 URL 與 接口 Class 創建 Invoker 調用對象。 4、proxyFactory.getProxy(invoker);
通過代理工廠創建遠程服務代理返回給使用者。
2、Procotol#refer
和服務暴露一樣,consumer 端進行服務調用的時候,可以對 dubbo 框架進行擴展:com.alibaba.dubbo.rpc.ExporterListener
與com.alibaba.dubbo.rpc.Filter
。所以獲取到的 Procotol 實例的結構是:
- ProtocolListenerWrapper
- ProtocolFilterWrapper
- RegistryProtocol
- ProtocolFilterWrapper
1、RegistryFactory#getRegistry
獲取到 zookeeper 註冊中心,和服務暴露獲取註冊中心的邏輯一樣。 2、創建註冊服務目錄 RegistryDirectory 3、註冊服務消費者 URL 到 zookeeper,其實就是創建 zookeeper 的節點,和服務端發佈類似。
/dubbo/com.alibaba.dubbo.demo.DemoService/consumers
/consumer%3A%2F%2F192.168.20.1%2Fcom.alibaba.dubbo.demo.DemoService%3Fapplication%3Ddemo-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D3808%26qos.port%3D33333%26side%3Dconsumer%26timestamp%3D1522590290256
4、訂閱 zookeeper 以下結點,當服務發生變更時,銷燬無效的 Invoke.刷新 RegistryDirectory 中的Map<String, List<Invoker<T>>> methodInvokerMap
對象。
- /dubbo/com.alibaba.dubbo.demo.DemoService/providers
- /dubbo/com.alibaba.dubbo.demo.DemoService/configurators
- /dubbo/com.alibaba.dubbo.demo.DemoService/routers
5、調用cluster#join(directory)
合併 invoker 創建並提供集羣 failover (故障轉移)調用策略
- cluster#join(directory)//加入集羣路由
- ExtensionLoader#getExtensionLoader(Cluster.class).getExtension("failover");
- MockClusterWrapper#join
- this.cluster#join(directory)
- FailoverCluster#join
- return new FailoverClusterInvoker<T>(directory)
- FailoverCluster#join
- this.cluster#join(directory)
- MockClusterWrapper#join
- ExtensionLoader#getExtensionLoader(Cluster.class).getExtension("failover");
3、DubboProtocol#refer
1、通過 接口Class對象、服務URL、ExchangeClient和Set<Invoker<?>> invokers
創建 DubboInvoke 對象 2、獲取 ExchangeClient 數據交換客戶端 HeaderExchangeClient,並創建心跳連接。默認是創建 Netty 客戶端用來調用暴露的遠程調用。 3、將創建的 invoker (服務調用者)返回給目錄服務,用來刷新 RegistryDirectory 中的Map<String, List<Invoker<T>>> methodInvokerMap
對象。
4、ProxyFactory#getProxy
通過代理工廠創建服務引用接口的代理對象,用於訪問暴露的遠程服務。
1、根據 dubbo SPI 機制默認獲取到 JavassistProxyFactory 對象 2、通過上面獲取到的 Invoke對象以及引用的遠程服務接口 + dubbo 裏面的 EchoService 調用AbstractProxyFactory#getProxy(Invoker<T>, Class<?>[])
獲取到代理對象。 3、使用 JDK 裏面的 InvocationHandler 對象代理遠程暴露服務 Invoke 調用對象創建遠程暴露服務接口代理對象。