在應用編寫好服務並進行之后,dubbo負責將服務export出去,dubbo export服務的時候主要做了以下幾件事:
- 將服務export到本地(根據scope的配置)
- 創建Invoker(啟動本地NettyServer,監聽指定端口,等待請求)
- 注冊provider的信息到registry,供consumer發現並訂閱服務
- 訂閱registry中的configurator節點,可以動態更改部分provider的配置
暴露服務的配置方式有:
- 直接通過API方式
- 通過xml配置
- 通過注解
ServiceBean加載和初始化
以常用的xml配置為例,前面說了dubbo的xml自定義標簽最后都是將對應的bean注入容器中,<dubbo:service /> 對應的就是ServiceBean,service暴露服務就在spring初始化ServiceBean的時候
<dubbo:service interface="com.test.service.TestDubboService" ref="testDubboServiceImpl"/>
dubbo解析xml會將對應的bean—ServiceBean注入到spring容器中,由於解析xml的時候配置的bean是非lazyInit的,所以在spring容器初始化完成之后,會初始化所有非lazyInit的bean。
在spring容器初始化后,會廣播ContextRefreshedEvent事件通知,ServiceBean實現了ApplicationListener,在收到該事件之后調用export方法
// AbstractApplicationContext
public void refresh() throws BeansException, IllegalStateException {
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 里面會廣播ContextRefreshedEvent事件
finishRefresh();
}
// ServiceBean
public void onApplicationEvent(ApplicationEvent event) {
// 在容器初始化完成之后收到ContextRefreshedEvent事件,開始export服務
if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
if (isDelay() && ! isExported() && ! isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
}
服務export從ServiceConfig#export開始
- export:判斷是否是延遲啟動,如果是延遲啟動則啟動守護線程,sleep指定時間之后再調用doExport,否則直接調用
- doExport:主要工作是檢查並配置
- 初始化application、registries、monitor等配置
- 判斷是否是泛化調用,如果不是,判斷interface和ref是否合法
- 檢查並配置application、registries、protocols、ServiceConfig本身,找對應配置類屬性的set方法,依次從System.getProperty(-D參數指定,或者set進去的配置)、dubbo.properties.file參數執行的配置文件文件中查找對應的屬性,然后調用set 方法配置
- 檢查是否有stub和mock配置,如果有配置,判斷配置是否合法。如果有stub配置:判斷stub類是否有該服務interface類型的構造方法;如果有mock配置:如果直接是"return"表達式,parse返回的value是否合法,否則需要判斷mockClass是否有無參的構造方法
- loadRegistries:加載所有的registries,可能會有多個注冊中心,查找所有的registries配置並返回符合條件的
- 針對每一個registry配置,從applicationConfig、registryConfig獲取參數拼裝registryUrl
- 條件是:如果是provider則register不能配置為false(可以不配置,如果配置了必須為true);如果不是provider,subscribe不能配置為false(可以不配置,如果配置了必須為true)。
- doExportUrls:由於dubbo支持多個協議,所以dubbo針對每一種協議都會在每一個注冊中心注冊一遍
- doExportUrlsFor1Protocol:根據配置來拼裝URL,在export的整個過程中靠URL來傳遞配置
- 獲取配置的協議名稱,如果沒有配置協議名稱,則使用默認協議:dubbo
- 獲取配置的ip:port,如果port沒有配置從默認端口20880開始依次向后尋找可用的port,如果port配置為-1表示隨機選取一個port
- 從application、module、provider、ptotocol等獲取配置,將這些配置加入到map中將來用來生成URL
- 判斷是否有dubbo:argument配置,將對應的配置也加入map
- 是否是泛化(Generic)調用,如果沒有則判斷是否有method級的配置
- 是否是injvm的調用,如果是,則不需要注冊到注冊中心
- 根據前面host:port、protocolName、map構造URL
- 判斷scope的配置,為none表示不暴露服務,配置為remote則export到local和remote,如果配置為local則只export到local
export服務到本地
如果scope沒有配置或者配置local、remote,dubbo會將服務export到本地,意思就是:將服務以injvm協議export出去,如果是同一個jvm的應用可以直接通過jvm發起調用,而不需要通過網絡發起遠程調用。
export到本地主要做了以下幾件事:
- 將url的協議配置為jvm,host:port配置為127.0.0.1:0
- 構造filter鏈,雖然是本地export,但是會經過定義好的filter
- 構造InjvmExporter
創建Invoker
如果scope沒有配置或者配置remote,dubbo會創建invoker,創建invoker的時候會啟動NettyServer,監聽指定的端口等待consumer請求。在dubbo中provider和consumer端都會有Invoker,實現的是同一個接口,但是不同的實現,invoker的意義就是服務的代理,provider側的invoker就是提供服務的可執行體,在netty接收到請求之后會通過invoker來處理,最后調用目標服務。
創建invoker並啟動NettyServer的調用堆棧
export的主要過程是:
- 創建Invoker,proxyFactory.getInvoker
- 創建filter鏈,ProtocolFilterWrapper#buildInvokerChain
- 調用配置的exporter的listener,ListenerExporterWrapper#ListenerExporterWrapper
- 啟動NettyServer,DubboProtocol#openServer
filter和listener是可擴展的,可以自己實現filter和listener,按照SPI方式配置好,dubbo會自動加載。
注冊到注冊中心並訂閱
前面只是完成service的啟動並具備可被請求的狀態,但是dubbo作為一個支持服務自發現的框架,還會把provider的信息注冊到registry,並且訂閱configurators。
注冊的調用堆棧是
注冊和訂閱主要邏輯在RegistryProtocol#export
- 啟動NettyServer
- 注冊providerUrl
- 訂閱configurators
registry就是一個目錄服務,注冊的過程也就是創建對應的目錄,並訂閱關心的目錄變化。provider會在registry中創建類似如下的目錄結構
其中provider注冊的url為,會創建com.foo.BarService、provider和providerUrl節點
dubbo/com.test.service.TestDubboService/providers/dubbo%3A%2F%2F192.168.0.102%3A20880%2Fcom.test.service.TestDubboService%3Fapplication%3Dcom.test.demo%26default.export%3Dtrue%26export%3Dtrue%26generic%3Dfalse%26interface%3Dcom.test.service.TestDubboService%26pid%3D45599%26side%3Dprovider%26timestamp%3D1515313385792
接下來會創建並訂閱configurators節點,訂閱的意思就是監聽configurators及其子節點,創建configurators的時候會添加listener,provider端監聽configurators的listener是:
com.alibaba.dubbo.registry.integration.RegistryProtocol.OverrideListener#notify
在治理中心中修改provider的配置的時候,注冊中心會通知監聽的listener,provider會進行相關配置。
總結
export服務是dubbo關鍵路徑中的第一步,此時dubbo已經具備了被consumer自動發現並調用的條件,接下來就是consumer發現服務。