該篇博客意在做dubbo啟動(dòng)調(diào)用流程做源碼分析,采用先給出部分結(jié)論,然后推導(dǎo)出整個(gè)調(diào)用流程的過程
創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供李滄網(wǎng)站建設(shè)、李滄做網(wǎng)站、李滄網(wǎng)站設(shè)計(jì)、李滄網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、李滄企業(yè)網(wǎng)站模板建站服務(wù),十載李滄做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。一? 服務(wù)發(fā)布dubbo服務(wù)的每個(gè)標(biāo)記了@service的類和在xml配置中帶有標(biāo)簽
首先看一下serviceBean的類繼承關(guān)系:
從類繼承圖中,我們發(fā)現(xiàn)了幾個(gè)重要的接口:
serviceConfig? ? ? ?這個(gè)是和dubbo服務(wù)發(fā)布相關(guān)的
ApplicationListener? ? spring的事件相關(guān)
InitializingBean? ? ? ?初始化相關(guān)
當(dāng)然最重要的就是ApplicationListener的監(jiān)聽方法了:
以下是服務(wù)發(fā)布的幾個(gè)重要路徑:
ServiceBean#export();
ServiceConfig#doExport()
doExportUrlsFor1Protocol()
在doExportUrlsFor1Protocol這里就是服務(wù)發(fā)布的重點(diǎn)了
以上截圖就是dubbo在做服務(wù)發(fā)布的核心代碼了,這里dubbo使用了動(dòng)態(tài)代理(SPI)+裝飾器模式,完整的構(gòu)造了一個(gè)Invoker和Exporter
DUBBO的SPI:ExtensionLoader? 通過加載指定路徑下,配置的key-value形式的類。同時(shí)還支持代理和增強(qiáng)(Wrapper + 裝飾),有興趣的同學(xué)可以認(rèn)真研究一下dubbo的SPI機(jī)制,這里只做簡(jiǎn)單概括,比如里面2個(gè)最重要的代理對(duì)象:proxyFacotory,protocol,他們的源碼分別截圖如下:
proxyFactory:
protocol:
從以上截圖不難發(fā)現(xiàn):protocol和proxyFactory的處理邏輯大體類似:通過url獲取指定參數(shù),如果為空,就使用默認(rèn)值:然后通過ExtensionLoader加載從url中獲取的對(duì)象
首先看一下通過proxyFactory獲取的Invoker對(duì)象:
想一下,為什么extName是javassist,獲取到的ProxyFactory卻是:StubProxyFactoryWrapper?按照之前說的SPI機(jī)制,應(yīng)該獲取的是:JavassistProxyFactory?
這是因?yàn)椋涸贓xtensionLoader#createExtension時(shí)
cachedWrapperClasses的寫入是在讀取指定路徑的文件時(shí)寫入的,通過判斷加載的類是否有,包含相同接口的構(gòu)造器做判斷的:
這里真正干活的還是:JavassistProxyFactory
所以,真正構(gòu)建Invoker的代碼如下:
其實(shí)就是構(gòu)建了一個(gè)AbstractProxyInvoker,其中的Wrapper簡(jiǎn)單的理解成一個(gè)包裝類即可(這就是dubbo動(dòng)態(tài)生成類的方式,其實(shí)沒有什么多大的技術(shù)問題,只是覺得就是裝X而已),代碼如下:
再來看看export過程:
同樣的,extName是registry,實(shí)際的加載類是:ProtocolListenerWrapper
而ProtocolListenerWrapper里面包裝的protocol是:ProtocolFilterWrapper,而ProtocolFilterWrapper里面包裝的protocol是RegistryProtocol(真正干活的)
可以看下最終生成的Exporter的結(jié)構(gòu):
其中DubboExporter是在DubboProtocol類中構(gòu)造的,還是和之前一樣,通過裝飾器,一層一層遞進(jìn)構(gòu)造的:
至此,服務(wù)的發(fā)布已經(jīng)完成,其中有些步驟跳躍可能稍微大一點(diǎn),感興趣的同學(xué)請(qǐng)自行本地調(diào)試?
二? 服務(wù)調(diào)用dubbo中每一個(gè)標(biāo)記了@Reference域和在xml中配置了
首先,看一下ReferenceBean的類關(guān)系:
其中FactoryBean就是spring的工廠Bean,和ServiceBean類似,ReferenceBean也繼承一個(gè)Config類,如上,服務(wù)調(diào)用的故事就從ReferenceConfig的#get()開始
ReferenceBean#getObject()
? ReferenceConfig#get()
init();
相信使用過動(dòng)態(tài)代理的同學(xué)都知道,使用動(dòng)態(tài)代理的使用,通過代理對(duì)象,在自己的邏輯里面就可以"為所欲為"了。dubbo在服務(wù)調(diào)用的時(shí)候也是這么干的!
init方法就是創(chuàng)建代理的入口,方法比較長(zhǎng),咱們撿重點(diǎn)說,前面就是一些校驗(yàn),判斷,最主要的方法入口:
創(chuàng)建代理需要一個(gè)map,看看這個(gè)map里面的信息如下:
下面正式開啟創(chuàng)建代理的過程,老規(guī)矩,忽略掉無關(guān)精要的代碼,直接上干貨:
這個(gè)refprotocol就是在前面截圖的那個(gè)Protocol$Adaptive代碼,這里咱們?yōu)榱朔奖阏{(diào)試,直接把代碼替換一下:
和服務(wù)發(fā)布過程一樣,extName是registry,獲取到的實(shí)例卻是:ProtocolListenerWrapper(裝飾器模式)
大概裝飾層級(jí)就是:
ProtocolListenerWrapper
?ProtocolFilterWrapper
RegistryProtocol
在RegistryProtocol是真正干活的地方:
一路debug調(diào)試,最終返回到創(chuàng)建代理的代碼入口:
在之前的服務(wù)發(fā)布過程中,我們已經(jīng)帖出了proxyFactory的相關(guān)代碼(dubbo這里做了動(dòng)態(tài)加載,不利于調(diào)試,咱們做個(gè)替換)
替換后的代碼如下:
現(xiàn)在將要進(jìn)行真正的動(dòng)態(tài)代理的創(chuàng)建過程:
和其他的Adaptive一樣,真正干活的是JavassistProxyFactory,使用StubProxyFactoryWrapper做裝飾
真正干活的是JavassistProxyFactory
看到這里,熟悉動(dòng)態(tài)代理的同學(xué)已經(jīng)很熟悉了。真正的執(zhí)行邏輯就在InvokerInvocationHandler里面了,在這個(gè)handler里面是不是為所欲為呢?
截圖如下:
由截圖可知,在InvokerInvocationHandler里面真正調(diào)用就是使用invoker調(diào)用邏輯
當(dāng)真正調(diào)用時(shí),一路dubug
這個(gè)Invoker也是一個(gè)裝飾器。咱們一步一步調(diào)試進(jìn)入:
首先:MockClusterInvoker
真正的調(diào)用又委托給了:FailoverClusterInvoker
從以上代碼注釋中可以看出,F(xiàn)ailoverClusterInvoker,通過負(fù)載均衡器選擇"最優(yōu)"的Invoker發(fā)起遠(yuǎn)程調(diào)用
這里選擇了:RegistryDirectory$DeleteInvoker其實(shí)就是一個(gè)InvokerWrapper
真正干活的是ListenerInvokerWrapper
繼續(xù)遞進(jìn):調(diào)用ProtocolFilterWrapper
最終來到了ProtocolFilterWrapper的匿名內(nèi)部類:
把調(diào)用過程委托給了filter調(diào)用鏈路,至此,Invoker的使命完成了。相信更深的調(diào)用邏輯,通過分析Filter就能找到答案了
三? FailOverClusterInvoker選擇Invoker過程既FilterChain調(diào)用鏈的組裝在第二節(jié)中,有分析服務(wù)發(fā)布調(diào)用過程,最紅是委托給了Filter調(diào)用。我們?cè)诜治龅臅r(shí)候,有意的忽略掉了部分調(diào)用細(xì)節(jié),只是為了更好的梳理出整個(gè)調(diào)用鏈路。下面就分析一下在負(fù)載均衡器選擇Invoker的詳細(xì)邏輯
故事的開始當(dāng)然是從FailoverClusterInvoker的invoke開始,FailoverClusterInvoker沒有自己實(shí)現(xiàn)invoke,其父類AbstractClusterInvoker實(shí)現(xiàn)了:
尋找Invoker的時(shí)候委托給了RegistryDirectory:
最終的Invoker尋找在RegistryDirectory里面維護(hù)的一個(gè)字段:methodInvokerMap里面查詢即可
查詢到全量的數(shù)據(jù)之后,就通過負(fù)載均衡器選擇,完成Invoker的選擇
那RegistryDirectory的methodInvokerMap又是在什么時(shí)候獲得調(diào)用方法和Invoker的對(duì)應(yīng)關(guān)系的呢?
通過跟蹤字段的調(diào)用來源
最終確認(rèn): RegistryProtocol#doRefer時(shí)會(huì)調(diào)用到如上方法里面,生成method和Invokers的對(duì)應(yīng)關(guān)系
以上,將服務(wù)發(fā)布,服務(wù)調(diào)用的過程做了較為粗略和完整的說明
四 結(jié)論證明在前言部分有說過,先給出部分結(jié)論:
1 所有被標(biāo)記了@service和使用
2 所有被標(biāo)記了@Reference和使用
這是怎么做到的呢?
在結(jié)合xml使用的時(shí)候,dubbo有引入DubboNamespaceHandler,該handler會(huì)解析相關(guān)的xml標(biāo)簽,完成如上功能:
而在springboot應(yīng)用中,dubbo有引入:ServiceAnnotationBeanPostProcessor
該類是一個(gè)前置處理器,會(huì)掃描@service標(biāo)記的類,通過構(gòu)建BeanDefinitionBuilder完成ServiceBean的注冊(cè):
同樣的:springboot應(yīng)用中引入了ReferenceAnnotationBeanPostProcessor
ReferenceAnnotationBeanPostProcessor是一個(gè)后置處理器,在Bean初始化時(shí)會(huì)被調(diào)用,注入相應(yīng)的代理類
創(chuàng)作不易,如果對(duì)你有幫助,請(qǐng)點(diǎn)贊+收藏!
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧