本節主要闡述如下兩個問題:
- Dubbo自定義标簽實作。
- dubbo通過Spring加載配置檔案後,是如何觸發注冊中心、服務提供者、服務消費者按照Dubbo的設計執行相關的功能。
所謂的執行相關功能如下:
- 注冊中心啟動,監聽消息提供者的注冊服務、接收消息消費者的服務訂閱(服務注冊與發現機制)。
- 服務提供者向注冊中心注冊服務。
- 服務消費者向注冊中心訂閱服務。
接下來從使用dubbo的角度,從配置檔案入手:
1、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應用程式名、注冊中心、協定、服務提供者參數預設值、服務提供者,這些配置後面的實作原理是什麼呢?是如何啟動并發揮相關作用的呢?
2、Spring自定義标簽即指令空間實作
dubbo自定義标簽與命名空間其實作代碼在子產品dubbo-config中,其核心實作如下:
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(基于注解),下文會詳細分析上述兩個解析類的實作。
2.2 定義dubbo.xsd 檔案
在dubbo-config-spring子產品下的 src/main/resouce/META-INF中分别定義dubbo.xsd、spring.handlers、spring.schemas。
關于Spring如何新增命名空間與标簽,在源碼分析ElasticJob時已經詳細介紹過,再這裡就不做 過多重複,如需了解,請檢視:
https://blog.csdn.net/prestigeding/article/details/797510233、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/80490206DubboBeanDefinitionParser構造函數如下:
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作為其預設參數呢?有兩種辦法:
- 将dubbo:service标簽直接聲明在dubbo:provider方法
- 在dubbo:service中通過provider屬性指定一個provider配置,如果不填,并且存在多個dubbo:provider配置,則會抛出錯誤。
- dubbo:customer:解析嵌套标簽,其原理與dubbo:provider解析一樣。
Step3:解析标簽,将屬性與值填充到BeanDefinition的propertyValues中。最終傳回BeanDefinition執行個體,供Spring執行個體化Bean。
上述已經解答了Dubbo自定義标簽的解析實作,主要完成了ApplicationConfig、RegistryConfig、ServiceBean、ReferenceBean執行個體的初始化,那什麼時候建構與注冊中心的連接配接、服務提供者什麼時候會向注冊中心注冊服務,服務消費者向注冊中心訂閱服務呢?
通過上述步驟,我們已經知道已經成功解析注冊中心、服務提供者、服務消費者的配置元資訊,并将其執行個體化,按照我們的思路,配置對象生成後,下一步應該是實作Dubbo服務的注冊與發現機制,但代碼中無法找到相關代碼。
據我目前所掌握的知識,Spring在對象執行個體化,一般有兩種方式來對Bean做一些定制化處理。
- 實作BeanPostProcessor Spring後置處理器,在Bean初始化前後執行相關操作。
- 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定義了如下三個方法:
- T getObject() throws Exception:擷取需要傳回的結果對象。
- Class<?> getObjectType():擷取傳回對象的類型。
- boolean isSingleton():傳回是否是單例。
看到這裡,不免有一點小激動,似乎已經摸到Dubbo服務注冊與發現機制(Dubbo服務提供者、Dubbo服務消費者、注冊中心的啟動流程入口點了,下一步就是分析ServiceBean、ReferenceBean的實作原理,試圖揭開Dubbo服務注冊與發現機制,該部分内容将在下一篇中詳細分析。
3.2 注解配置方式解析
注解配置方式的解析入口類:AnnotationBeanDefinitionParser,也是基于Spring注解解析邏輯,這部分在将在未來《Spring系列進階篇-源碼分析注解解析實作原理》中詳細分析,目前暫未深究,讀者朋友們,如果有興趣,可以以AnnotationBeanDefinitionParser為入口,進行進一步的分析。
本節就講解到這裡了,下一篇将重點分析ServiceBean(服務提供者啟動流程)。
原文釋出時間為:2019-01-24
本文作者:丁威,《RocketMQ技術内幕》作者。
本文來自
中間件興趣圈,了解相關資訊可以關注
。