源碼分析Dubbo前置篇-尋找註冊中心、服務提供者、服務消費者功能入口

本節主要闡述以下兩個問題:spring

  1. Dubbo自定義標籤實現。
  2. dubbo經過Spring加載配置文件後,是如何觸發註冊中心、服務提供者、服務消費者按照Dubbo的設計執行相關的功能。

所謂的執行相關功能以下:api

  • 註冊中心啓動,監聽消息提供者的註冊服務、接收消息消費者的服務訂閱(服務註冊與發現機制)。
  • 服務提供者向註冊中心註冊服務。
  • 服務消費者向註冊中心訂閱服務。

接下來從使用dubbo的角度,從配置文件入手:架構

一、Dubbo服務提供者的通常配置以下:

<!-- 提供方應用信息,用於計算依賴關係 -->
<dubbo:application name="uop" owner="uce" />
<!-- 使用zookeeper註冊中心暴露服務地址 -->
<dubbo:registry protocol="zookeeper" address="zookeeper://192.168.xx.xx:2181?backup=192.168.xx.xx:2182,192.168.xx.xx:2183" />
<!--dubbox中引入Kryo和FST這兩種高效Java序列化實現,來逐步取代原生dubbo中的hessian2,若是使用kryo記得添加依賴 -->
<dubbo:protocol name="dubbo" serialization="kryo" port="20990" />
<!-- 定義服務提供者默認屬性值 -->
<dubbo:provider timeout="5000" threadpool="fixed" threads="100" accepts="1000" token="true" />
<!-- 暴露服務接口 一個服務能夠用多個協議暴露,一個服務也能夠註冊到多個註冊中心-->
<!--Provider上儘可能多配置Consumer端的屬性,讓Provider實現者一開始就思考Provider服務特色、服務質量的問題-->
<dubbo:service interface="com.yingjun.dubbox.api.UserService" ref="userService" />

上面經過dubbo提供的dubbo:application、dubbo:registry、dubbo:protocol、dubbo:provider、dubbo:service分別定義dubbo應用程序名、註冊中心、協議、服務提供者參數默認值、服務提供者,這些配置後面的實現原理是什麼呢?是如何啓動併發揮相關做用的呢?併發

二、Spring自定義標籤即命令空間實現

dubbo自定義標籤與命名空間其實現代碼在模塊dubbo-config中,其核心實現以下。app

2.1 DubboNamespaceHandler

dubbo命名空間實現handler,其全路徑:com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler,其源碼實現以下:分佈式

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }
}

從這裏能夠看出,dubbo自定義的標籤主要包括:application、module、registry、monitor、provider、consumer、protocol、service、reference、annotation,其具體解析實現類主要包括:DubboBeanDefinitionParser(基於xml配置文件)、AnnotationBeanDefinitionParser(基於註解),下文會詳細分析上述兩個解析類的實現。ide

2.2 定義dubbo.xsd 文件

在dubbo-config-spring模塊下的 src/main/resouce/META-INF中分別定義dubbo.xsd、spring.handlers、spring.schemas。 關於Spring如何新增命名空間與標籤,在源碼分析ElasticJob時已經詳細介紹過,再這裏就不作 過多重複,如需瞭解,請查看:http://www.javashuo.com/article/p-pptkrtcg-hp.html函數

三、Bean解析機制(Spring基礎知識)

咱們應該知道,Spirng的配置支持xml配置文件與註解的方式,故Dubbo也支持兩種配置方式,xml與註解方式。高併發

3.1 xml配置方式解析

這裏寫圖片描述

BeanDefinitionParser:Spring定義的bean解析器,要實現自定義標籤,則須要實現該接口,而後經過NamespaceHandlerSupport將Bean定義解析器註冊到Spring bean解析器中。從接口中能夠看出,其終極目標就是將Element element(xml節點)解析成BeanDefinition,有關於Spring BeanDefinition,請參考:https://blog.csdn.net/prestigeding/article/details/80490206 DubboBeanDefinitionParser構造函數以下:源碼分析

public DubboBeanDefinitionParser(Class<!--?--> beanClass, boolean required) {
        this.beanClass = beanClass;
        this.required = required;
}

beanClass:該xml標籤節點最終會被Spring實例化的類名。 required:該標籤的ID是否必須。 標籤名稱 |類名

  • | :-: | -: dubbo:application | ApplicationConfig dubbo:module | ModuleConfig dubbo:registry | RegistryConfig dubbo:monitor| MonitorConfig dubbo:provider | ProviderConfig dubbo:consumer| ConsumerConfig dubbo:protocol| ProtocolConfig dubbo:service| ServiceBean dubbo:reference | ReferenceBean 注,包名:com.alibaba.dubbo.config bean解析器的主要目的就是將上述標籤,解析成對應的BeanDifinition,以便Spring構建上述類的實例。 本節不拷貝DubboBeanDefinitionParser根據xml定義的標籤與屬性轉換成BeanDefinitionParser的每一行代碼,本節只給出其大致關鍵點。 Step1:解析id屬性,若是DubboBeanDefinitionParser對象的required屬性爲true,若是id爲空,則根據以下規則構建一個id。

  • 若是name屬性不爲空,則取name的值,若是已存在,則爲 name + 序號,例如 name,name1,name2。

  • 若是name屬性爲空,若是是dubbo:protocol標籤,則取protocol屬性,其餘的則取interface屬性,若是不爲空,則取該值,但若是已存在,和name處理相同,在後面追加序號。

  • 若是第二步還未空,則取beanClass的名稱,若是已存在,則追加序號。

Step2:根據不一樣的標籤解析特殊屬性。

  • dubbo:protocol,添加protocol屬性(BeanDefinition)。
  • dubbo:service,添加ref屬性。
  • dubbo:provider,嵌套解析,dubbo:provider標籤有兩個可選的子標籤,dubbo:service、dubbo:parameter,這裏須要嵌套解析dubbo:service標籤 知識點:dubbo:provider是配置服務提供者的默認參數,在dubbo spring配置文件中能夠配置多個dubbo:provider,那dubbo:service標籤如何選取一個合適的dubbo:provider做爲其默認參數呢?有兩種辦法:
  1. 將dubbo:service標籤直接聲明在dubbo:provider方法
  2. 在dubbo:service中經過provider屬性指定一個provider配置,若是不填,而且存在多個dubbo:provider配置,則會拋出錯誤。
  • dubbo:customer:解析嵌套標籤,其原理與dubbo:provider解析同樣。

Step3:解析標籤,將屬性與值填充到BeanDefinition的propertyValues中。最終返回BeanDefinition實例,供Spring實例化Bean。

上述已經解答了Dubbo自定義標籤的解析實現,主要完成了ApplicationConfig、RegistryConfig、ServiceBean、ReferenceBean實例的初始化,那何時構建與註冊中心的鏈接、服務提供者何時會向註冊中心註冊服務,服務消費者向註冊中心訂閱服務呢?

經過上述步驟,咱們已經知道已經成功解析註冊中心、服務提供者、服務消費者的配置元信息,並將其實例化,按照咱們的思路,配置對象生成後,下一步應該是實現Dubbo服務的註冊與發現機制,但代碼中沒法找到相關代碼。

據我目前所掌握的知識,Spring在對象實例化,通常有兩種方式來對Bean作一些定製化處理。

  1. 實現BeanPostProcessor Spring後置處理器,在Bean初始化先後執行相關操做。
  2. Bean實現InitializingBean接口(init-method)

瀏覽表格中全部Bean的聲明,發現了兩個類很是特殊: 這裏寫圖片描述

ServiceBean(服務提供者)與ReferenceBean(服務消費者)比較特殊,實現了Spring與Bean生命週期相關的接口。

  • InitializingBean,其聲明的接口爲afterPropertiesSet方法,顧名思義,就是在bean初始化全部屬性以後調用。
  • DisposableBean:其聲明的接口爲destroy()方法,在Spring BeanFactory銷燬一個單例實例以前調用。
  • ApplicationContextAware:其聲明的接口爲void setApplicationContext(ApplicationContext applicationContext),實現了該接口,Spring容器在初始化Bean時會調用該方法,注入ApplicationContext,已方便該實例能夠直接調用applicationContext獲取其餘Bean。
  • ApplicationListener< ContextRefreshedEvent>:容器從新刷新時執行事件函數。
  • BeanNameAware:其聲明的接口爲:void setBeanName(String name),實現該接口的Bean,其實例能夠獲取該實例在BeanFactory的id或name。
  • FactoryBean:Spring初始化Bean的另一種方式,例如dubbo:reference,須要返回的對象並非ReferenceBean,而是要返回ref指定的代理類來執行業務操做,故這裏使用FactoryBean很是合適,FactoryBean定義了以下三個方法:
  1. T getObject() throws Exception:獲取須要返回的結果對象。
  2. Class<!--?--> getObjectType():獲取返回對象的類型。
  3. boolean isSingleton():返回是不是單例。

看到這裏,難免有一點小激動,彷佛已經摸到Dubbo服務註冊與發現機制(Dubbo服務提供者、Dubbo服務消費者、註冊中心的啓動流程入口點了,下一步就是分析ServiceBean、ReferenceBean的實現原理,試圖揭開Dubbo服務註冊與發現機制,該部份內容將在下一篇中詳細分析。

3.2 註解配置方式解析

註解配置方式的解析入口類:AnnotationBeanDefinitionParser,也是基於Spring註解解析邏輯,這部分在將在將來《Spring系列進階篇-源碼分析註解解析實現原理》中詳細分析,目前暫未深究,讀者朋友們,若是有興趣,能夠以AnnotationBeanDefinitionParser爲入口,進行進一步的分析。

本節就講解到這裏了,下一篇將重點分析ServiceBean(服務提供者啓動流程)。


做者介紹:丁威,《RocketMQ技術內幕》做者,RocketMQ 社區佈道師,公衆號:中間件興趣圈 維護者,目前已陸續發表源碼分析Java集合、Java 併發包(JUC)、Netty、Mycat、Dubbo、RocketMQ、Mybatis等源碼專欄。能夠點擊連接:中間件知識星球,一塊兒探討高併發、分佈式服務架構,交流源碼。

相關文章
相關標籤/搜索