dubbo源碼—service export


在應用編寫好服務並進行之后,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開始

  1. export:判斷是否是延遲啟動,如果是延遲啟動則啟動守護線程,sleep指定時間之后再調用doExport,否則直接調用
  2. doExport:主要工作是檢查並配置
    1. 初始化application、registries、monitor等配置
    2. 判斷是否是泛化調用,如果不是,判斷interface和ref是否合法
    3. 檢查並配置application、registries、protocols、ServiceConfig本身,找對應配置類屬性的set方法,依次從System.getProperty(-D參數指定,或者set進去的配置)、dubbo.properties.file參數執行的配置文件文件中查找對應的屬性,然后調用set 方法配置
    4. 檢查是否有stub和mock配置,如果有配置,判斷配置是否合法。如果有stub配置:判斷stub類是否有該服務interface類型的構造方法;如果有mock配置:如果直接是"return"表達式,parse返回的value是否合法,否則需要判斷mockClass是否有無參的構造方法
  3. loadRegistries:加載所有的registries,可能會有多個注冊中心,查找所有的registries配置並返回符合條件的
    1. 針對每一個registry配置,從applicationConfig、registryConfig獲取參數拼裝registryUrl
    2. 條件是:如果是provider則register不能配置為false(可以不配置,如果配置了必須為true);如果不是provider,subscribe不能配置為false(可以不配置,如果配置了必須為true)。
  4. doExportUrls:由於dubbo支持多個協議,所以dubbo針對每一種協議都會在每一個注冊中心注冊一遍
  5. doExportUrlsFor1Protocol:根據配置來拼裝URL,在export的整個過程中靠URL來傳遞配置
    1. 獲取配置的協議名稱,如果沒有配置協議名稱,則使用默認協議:dubbo
    2. 獲取配置的ip:port,如果port沒有配置從默認端口20880開始依次向后尋找可用的port,如果port配置為-1表示隨機選取一個port
    3. 從application、module、provider、ptotocol等獲取配置,將這些配置加入到map中將來用來生成URL
    4. 判斷是否有dubbo:argument配置,將對應的配置也加入map
    5. 是否是泛化(Generic)調用,如果沒有則判斷是否有method級的配置
    6. 是否是injvm的調用,如果是,則不需要注冊到注冊中心
    7. 根據前面host:port、protocolName、map構造URL
    8. 判斷scope的配置,為none表示不暴露服務,配置為remote則export到local和remote,如果配置為local則只export到local

export服務到本地

如果scope沒有配置或者配置local、remote,dubbo會將服務export到本地,意思就是:將服務以injvm協議export出去,如果是同一個jvm的應用可以直接通過jvm發起調用,而不需要通過網絡發起遠程調用。

export到本地主要做了以下幾件事:

  1. 將url的協議配置為jvm,host:port配置為127.0.0.1:0
  2. 構造filter鏈,雖然是本地export,但是會經過定義好的filter
  3. 構造InjvmExporter

創建Invoker

如果scope沒有配置或者配置remote,dubbo會創建invoker,創建invoker的時候會啟動NettyServer,監聽指定的端口等待consumer請求。在dubbo中provider和consumer端都會有Invoker,實現的是同一個接口,但是不同的實現,invoker的意義就是服務的代理,provider側的invoker就是提供服務的可執行體,在netty接收到請求之后會通過invoker來處理,最后調用目標服務。

創建invoker並啟動NettyServer的調用堆棧

export的主要過程是:

  1. 創建Invoker,proxyFactory.getInvoker
  2. 創建filter鏈,ProtocolFilterWrapper#buildInvokerChain
  3. 調用配置的exporter的listener,ListenerExporterWrapper#ListenerExporterWrapper
  4. 啟動NettyServer,DubboProtocol#openServer

filter和listener是可擴展的,可以自己實現filter和listener,按照SPI方式配置好,dubbo會自動加載。

注冊到注冊中心並訂閱

前面只是完成service的啟動並具備可被請求的狀態,但是dubbo作為一個支持服務自發現的框架,還會把provider的信息注冊到registry,並且訂閱configurators。

注冊的調用堆棧是

注冊和訂閱主要邏輯在RegistryProtocol#export

  1. 啟動NettyServer
  2. 注冊providerUrl
  3. 訂閱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發現服務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM