1. 程式人生 > >dubbo源碼分析 之 服務本地暴露

dubbo源碼分析 之 服務本地暴露

ice oca IE exec 規則 PE nbsp dpa tzu

dubbo 在服務暴露發生了哪些事,今天我們就來分析一下整個服務暴露中的本地暴露。本地暴露需要服務提供方與服務消費方在同一個 JVM。下面我們來寫一個本地暴露使用的例子:

1 DemoService.java

技術分享圖片

2DemoServiceImpl.java

技術分享圖片

3application.xml – Spring配置文件

技術分享圖片

4Provider.java – 調用本地暴露的服務

技術分享圖片

使用context.getBean("demoServiceDubbo", DemoService.class)這種方式來獲取Bean還不如使用context.getBean("demoService", DemoService.class)

來獲取真正對象。前一種方式獲取到是 dubbo 返回的代理類,其中可以獲取到 dubbo 的InvokerListenerFilter這兩個擴展點。可能這是與整個服務保持統一性吧。(大家如果有不同的觀點歡迎留言)。下面我們就從源碼的角度來分析一下 dubbo 的本地暴露吧。

如果大家看過dubbo官網的API配置(建議大家分析碼源的時候都使用API的形式調用,盡量少的引入第三方Jar包比如xml配置dubbo),我們就可以知道。dubbo服務的暴露的起點是ServiceConfig#export。下面我們就以這個方法以起點來分析它:技術分享圖片export這個方法就是dubbo服務暴露的入口,主要就是判斷這個服務是否暴露以及通過ScheduledThreadPoolExecutor

這個線程池類支持延遲暴露。技術分享圖片接下來就是doExport方法,在這個方法的前面就是做一些 check 操作,不是重點,就不一一分析了。我們主要看一下它的appendProperties方法以及doExportUrls這兩個方法。appendProperties()方法主要是為當前對象通過setter 方法來添加屬性,它主要是通過以下方式來添加屬性:

  • 從 System 中獲取屬性key值的優先通訊是: dubbo.provider. + 當前類 Id + 當前屬性名稱 > dubbo.provider. + 當前屬性名稱 為 key 獲取值.

  • 首先從特定properties文件加載屬性:首先 System.getProperty("dubbo.properties.file")

    獲取到文件路徑,如果獲取不到就會試圖加載 dubbo 的默認的路徑 dubbo.properties加載。獲取屬性的 key 和上面從 System 裏面獲取的規則一樣。

然後我們就來分析一下doExportUrl這個方法,因為 Dubbo 允許配置多協議,在不同服務上支持不同協議或者同一服務上同時支持多種協議。所以這裏需要循環各個協議進行多協議暴露服務。

技術分享圖片

然後我們來分析一下ServiceConfig#doExportUrlsFor1Protocol,首先我們來看一下appendParameters(Map, Object)這個方法,這個方法的作用就是通過調用當前對象的getter方法獲取到傳入對象的值然後塞到 map 當中去。用於後面的構造URL這個對象。

技術分享圖片

註:URL,配置信息的統一格式,所有擴展點都通過傳遞 URL 攜帶配置信息)技術分享圖片

技術分享圖片

如果大家沒有看過我6、dubbo源碼分析 之 服務暴露概述在這點我再向大家提醒一下,dubbo的對象扭轉過程是:

服務配置類 –> Invoker –> Exporter

首先通過 ref (服務實現類)、服務接口類以及 URL 默認通過 javassist,也就是 JavassistProxyFactory類獲取到代理對象。

技術分享圖片

然後再把 Invoker 對象轉化成Exporter對象。還記得之前的5.dubbo源碼分析 之 SPI分析之前是分析的遠程暴露中的獲取Protocol 實例。只是這裏的 protocol實例是 本地方法暴露獲取的實例。它也是 dubbo 自定義的 SPI 生成的 Protocol$Adaptive通過它的getExtension(name)方法創建 Protocol實例,然後通過 Protocol#export 方法獲取Exporter。

技術分享圖片

技術分享圖片

dubbo 的定義 SPI 裏面包括 AOP,其實就是獲取到所有的 SPI 接口的實例對象。然後在調用 getExtension(name)方法返回指定名字的擴展的時候會判斷哪些實現類的構造器只包含 SPI 接口就會進行代理。這裏的name是從 URL 中獲取協議。在調用ServiceConfig#loadRegistries方法的裏面返回的 URL 格式為registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?xxx=xxx然後再調用ServiceConfig#exportLocal(URL)方法的時候裏面把協議(protocol)設置成injvm, 然後 在 Protocol$Adaptive 進行服務暴露的時候:

技術分享圖片

因為 duubo 默認在dubbo-rpc-injvm的自定義Protocol配置文件(${dubbo-rpc-injvm}/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol)配置的是injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol所以得到的對象是InjvmProtocol。因為ProtocolListenerWrapper 與 ProtocolFilterWrapper 其實都包括 SPI 接口 Protocol的構造器。所以創建出來的對象如上所以。關於 dubbo 的SPI機制可以參看 – 2.dubbo源碼分析 之 內核SPI實現.

ProtocolListenerWrapper : 通過 SPI 機制獲取到 dubbo 的自定義擴展 InvokerListener。

ProtocolFilterWrapper : 通過 SPI 機制獲取到 dubbo 的自定義擴展 Filter(常用)。

其實關於 dubbo 通過 SPI 機制獲取機制獲取到 dubbo 的自定義擴展 Filter,還蠻復雜與重要的。在這裏就不多說了,感興趣的朋友可以下去參看源碼。

最終就到了InjvmProtocol暴露服務,其實它就是創建一個 InjvmExporter 對象返回。裏面包括 4 個屬性:

invoker:Invoker 對象實例

key:服務 key 值,在進行服務調用的時候會根據這個 key 值。獲取到當前服務暴露生成的 Exporter 對象。

exporterMap:服務 key 值與當前 Exporter 的映射

最後這個生成的 Exporter 最終會返回添加到 ServiceConfig的 exporters 屬性當中去。這樣就完成了 dubbo 服務暴露的本地暴露的過程。

技術分享圖片

其實整個 dubbo 的本地暴露邏輯還是蠻簡單的,把它分開來主要還是整個服務暴露過程比較復雜。先給大家講解一下本地暴露。如果大家清楚了本地暴露,然後再理解遠程暴露就會更加容易。

技術分享圖片

dubbo源碼分析 之 服務本地暴露