一、Dubbo整體架構(gòu)
1、Dubbo與Spring的整合
Dubbo在使用上可以做到非常簡(jiǎn)單,不管是Provider還是Consumer都可以通過(guò)Spring的配置文件進(jìn)行配置,配置完之后,就可以像使用spring bean一樣進(jìn)行服務(wù)暴露和調(diào)用了,完全看不到dubbo api的存在。這是因?yàn)閐ubbo使用了spring提供的可擴(kuò)展Schema自定義配置支持。在spring配置文件中,可以像、這樣進(jìn)行配置。META-INF下的spring.handlers文件中指定了dubbo的xml解析類:DubboNamespaceHandler。像前面的被解析成ServiceConfig,被解析成ReferenceConfig等等。
2、jdk spi擴(kuò)展
由于Dubbo是開(kāi)源框架,必須要提供很多的可擴(kuò)展點(diǎn)。Dubbo是通過(guò)擴(kuò)展jdk spi機(jī)制來(lái)實(shí)現(xiàn)可擴(kuò)展的。具體來(lái)說(shuō),就是在META-INF目錄下,放置文件名為接口全稱,文件中為key、value鍵值對(duì),value為具體實(shí)現(xiàn)類的全類名,key為標(biāo)志值。由于dubbo使用了url總線的設(shè)計(jì),即很多參數(shù)通過(guò)URL對(duì)象來(lái)傳遞,在實(shí)際中,具體要用到哪個(gè)值,可以通過(guò)url中的參數(shù)值來(lái)指定。
Dubbo對(duì)spi的擴(kuò)展是通過(guò)ExtensionLoader來(lái)實(shí)現(xiàn)的,查看ExtensionLoader的源碼,可以看到Dubbo對(duì)jdk spi做了三個(gè)方面的擴(kuò)展:
(1)jdk spi僅僅通過(guò)接口類名獲取所有實(shí)現(xiàn),而ExtensionLoader則通過(guò)接口類名和key值獲取一個(gè)實(shí)現(xiàn);
(2)Adaptive實(shí)現(xiàn),就是生成一個(gè)代理類,這樣就可以根據(jù)實(shí)際調(diào)用時(shí)的一些參數(shù)動(dòng)態(tài)決定要調(diào)用的類了。
(3)自動(dòng)包裝實(shí)現(xiàn),這種實(shí)現(xiàn)的類一般是自動(dòng)激活的,常用于包裝類,比如Protocol的兩個(gè)實(shí)現(xiàn)類:ProtocolFilterWrapper、ProtocolListenerWrapper。
3、url總線設(shè)計(jì)
Dubbo為了使得各層解耦,采用了url總線的設(shè)計(jì)。我們通常的設(shè)計(jì)會(huì)把層與層之間的交互參數(shù)做成Model,這樣層與層之間溝通成本比較大,擴(kuò)展起來(lái)也比較麻煩。因此,Dubbo把各層之間的通信都采用url的形式。比如,注冊(cè)中心啟動(dòng)時(shí),參數(shù)的url為:
registry://0.0.0.0:9090?codec=registry&transporter=netty
這就表示當(dāng)前是注冊(cè)中心,綁定到所有ip,端口是9090,解析器類型是registry,使用的底層網(wǎng)絡(luò)通信框架是netty。
二、Dubbo啟動(dòng)過(guò)程
Dubbo分為注冊(cè)中心、服務(wù)提供者(provider)、服務(wù)消費(fèi)者(consumer)三個(gè)部分。
1、注冊(cè)中心啟動(dòng)過(guò)程
注冊(cè)中心的啟動(dòng)過(guò)程,主要看兩個(gè)類:RegistrySynchronizer、RegistryReceiver,兩個(gè)類的初始化方法都是start。
RegistrySynchronizer的start方法:
(1)把所有配置信息load到內(nèi)存;
(2)把當(dāng)前注冊(cè)中心信息保存到數(shù)據(jù)庫(kù);
(3)啟動(dòng)5個(gè)定時(shí)器。
5個(gè)定時(shí)器的功能是:
(1)AutoRedirectTask,自動(dòng)重定向定時(shí)器。默認(rèn)1小時(shí)運(yùn)行1次。如果當(dāng)前注冊(cè)中心的連接數(shù)高于平均值的1.2倍,則將多出來(lái)的連接數(shù)重定向到其他注冊(cè)中心上,以達(dá)到注冊(cè)中心集群的連接數(shù)均衡。
(2)DirtyCheckTask,臟數(shù)據(jù)檢查定時(shí)器。作用是:分別檢查緩存provider、數(shù)據(jù)庫(kù)provider、緩存consumer、數(shù)據(jù)庫(kù)consumer的數(shù)據(jù),清除臟數(shù)據(jù);清理不存活的provider和consumer數(shù)據(jù);對(duì)于緩存中的存在的provider或consumer而數(shù)據(jù)庫(kù)不存在,重新注冊(cè)和訂閱。
(3)ChangedClearTask,changes變更表的定時(shí)清理任務(wù)。作用是讀取changes表,清除過(guò)期數(shù)據(jù)。
(4)AlivedCheckTask,注冊(cè)中心存活狀態(tài)定時(shí)檢查,會(huì)定時(shí)更新registries表的expire字段,用以判斷注冊(cè)中心的存活狀態(tài)。如果有新的注冊(cè)中心,發(fā)送同步消息,將當(dāng)前所有注冊(cè)中心的地址通知到所有客戶端。
(5)ChangedCheckTask,變更檢查定時(shí)器。檢查changes表的變更,檢查類型包括:參數(shù)覆蓋變更、路由變更、服務(wù)消費(fèi)者變更、權(quán)重變更、負(fù)載均衡變更。
RegistryReceiver的start方法:?jiǎn)?dòng)注冊(cè)中心服務(wù)。默認(rèn)使用netty框架,綁定本機(jī)的9090端口。最后啟動(dòng)服務(wù)的過(guò)程是在NettyServer來(lái)完成的。接收消息時(shí),拋開(kāi)dubbo協(xié)議的解碼器,調(diào)用類的順序是
NettyHandler-》NettyServer-》MultiMessageHandler-》HeartbeatHandler-》AllDispatcher-》 DecodeHandler-》HeaderExchangeHandler-》RegistryReceiver-》RegistryValidator-》RegistryFailover-》RegistryExecutor。
2、provider啟動(dòng)過(guò)程
provider的啟動(dòng)過(guò)程是從ServiceConfig的export方法開(kāi)始進(jìn)行的,具體步驟是:
(1)進(jìn)行本地jvm的暴露,不開(kāi)放任何端口,以提供injvm這種形式的調(diào)用,這種調(diào)用只是本地調(diào)用,不涉及進(jìn)程間通信。
(2)調(diào)用RegistryProtocol的export。
(3)調(diào)用DubboProtocol的export,默認(rèn)開(kāi)啟20880端口,用以提供接收consumer的遠(yuǎn)程調(diào)用服務(wù)。
(4)通過(guò)新建RemoteRegistry來(lái)建立與注冊(cè)中心的連接。
(5)將服務(wù)地址注冊(cè)到注冊(cè)中心。
(6)去注冊(cè)中心訂閱自己的服務(wù)。
3、consumer啟動(dòng)過(guò)程
consumer的啟動(dòng)過(guò)程是通過(guò)ReferenceConfig的get方法進(jìn)行的,具體步驟是:
(1)通過(guò)新建RemoteRegistry來(lái)建立與注冊(cè)中心的連接。
(2)新建RegistryDirectory并向注冊(cè)中心訂閱服務(wù),RegistryDirectory用以維護(hù)注冊(cè)中心獲取的服務(wù)相關(guān)信息。
(3)創(chuàng)建代理類,發(fā)起consumer遠(yuǎn)程調(diào)用時(shí),實(shí)際調(diào)用的是InvokerInvocationHandler。
三、實(shí)際調(diào)用過(guò)程
consumer端發(fā)起調(diào)用時(shí),實(shí)際調(diào)用經(jīng)過(guò)的類是:
1、consumer:
InvokerInvocationHandler-》MockClusterInvoker(如果配置了Mock,則直接調(diào)用本地Mock類)-》FailoverClusterInvoker(負(fù)載均衡,容錯(cuò)機(jī)制,默認(rèn)在發(fā)生錯(cuò)誤的情況下,進(jìn)行兩次重試)-》RegistryDirectory$InvokerDelegete-》ConsumerContextFilter-》FutureFilter->DubboInvoker
2、provider:
NettyServer-》MultiMessageHandler-》HeartbeatHandler-》AllDispatcher-》DecodeHandler-》HeaderExchangeHandler-》DubboProtocol.requestHandler-》EchoFilter-》ClassLoaderFilter-》GenericFilter-》ContextFilter-》ExceptionFilter-》TimeoutFilter-》MonitorFilter-》TraceFilter-》實(shí)際service。
四、Dubbo使用的設(shè)計(jì)模式
1、工廠模式
ServiceConfig中有個(gè)字段,代碼是這樣的:
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Dubbo里有很多這種代碼。這也是一種工廠模式,只是實(shí)現(xiàn)類的獲取采用了jdk spi的機(jī)制。這么實(shí)現(xiàn)的優(yōu)點(diǎn)是可擴(kuò)展性強(qiáng),想要擴(kuò)展實(shí)現(xiàn),只需要在classpath下增加個(gè)文件就可以了,代碼零侵入。另外,像上面的Adaptive實(shí)現(xiàn),可以做到調(diào)用時(shí)動(dòng)態(tài)決定調(diào)用哪個(gè)實(shí)現(xiàn),但是由于這種實(shí)現(xiàn)采用了動(dòng)態(tài)代理,會(huì)造成代碼調(diào)試比較麻煩,需要分析出實(shí)際調(diào)用的實(shí)現(xiàn)類。
2、裝飾器模式
Dubbo在啟動(dòng)和調(diào)用階段都大量使用了裝飾器模式。以Provider提供的調(diào)用鏈為例,具體的調(diào)用鏈代碼是在ProtocolFilterWrapper的buildInvokerChain完成的,具體是將注解中含有g(shù)roup=provider的Filter實(shí)現(xiàn),按照order排序,最后的調(diào)用順序是
EchoFilter-》ClassLoaderFilter-》GenericFilter-》ContextFilter-》ExceptionFilter-》 TimeoutFilter-》MonitorFilter-》TraceFilter。
更確切地說(shuō),這里是裝飾器和責(zé)任鏈模式的混合使用。例如,EchoFilter的作用是判斷是否是回聲測(cè)試請(qǐng)求,是的話直接返回內(nèi)容,這是一種責(zé)任鏈的體現(xiàn)。而像ClassLoaderFilter則只是在主功能上添加了功能,更改當(dāng)前線程的ClassLoader,這是典型的裝飾器模式。
3、觀察者模式
Dubbo的provider啟動(dòng)時(shí),需要與注冊(cè)中心交互,先注冊(cè)自己的服務(wù),再訂閱自己的服務(wù),訂閱時(shí),采用了觀察者模式,開(kāi)啟一個(gè)listener。注冊(cè)中心會(huì)每5秒定時(shí)檢查是否有服務(wù)更新,如果有更新,向該服務(wù)的提供者發(fā)送一個(gè)notify消息,provider接受到notify消息后,即運(yùn)行NotifyListener的notify方法,執(zhí)行監(jiān)聽(tīng)器方法。
4、動(dòng)態(tài)代理模式
Dubbo擴(kuò)展jdk spi的類ExtensionLoader的Adaptive實(shí)現(xiàn)是典型的動(dòng)態(tài)代理實(shí)現(xiàn)。Dubbo需要靈活地控制實(shí)現(xiàn)類,即在調(diào)用階段動(dòng)態(tài)地根據(jù)參數(shù)決定調(diào)用哪個(gè)實(shí)現(xiàn)類,所以采用先生成代理類的方法,能夠做到靈活的調(diào)用。生成代理類的代碼是ExtensionLoader的createAdaptiveExtensionClassCode方法。代理類的主要邏輯是,獲取URL參數(shù)中指定參數(shù)的值作為獲取實(shí)現(xiàn)類的key。