日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
女朋友問(wèn)我:Dubbo的服務(wù)引用過(guò)程

本文轉(zhuǎn)載自微信公眾號(hào)「三太子敖丙」,作者三太子敖丙 。轉(zhuǎn)載本文請(qǐng)聯(lián)系三太子敖丙公眾號(hào)。

創(chuàng)新互聯(lián)公司,為您提供成都網(wǎng)站建設(shè)成都網(wǎng)站制作、網(wǎng)站營(yíng)銷(xiāo)推廣、網(wǎng)站開(kāi)發(fā)設(shè)計(jì),對(duì)服務(wù)成都小攪拌車(chē)等多個(gè)行業(yè)擁有豐富的網(wǎng)站建設(shè)及推廣經(jīng)驗(yàn)。創(chuàng)新互聯(lián)公司網(wǎng)站建設(shè)公司成立于2013年,提供專(zhuān)業(yè)網(wǎng)站制作報(bào)價(jià)服務(wù),我們深知市場(chǎng)的競(jìng)爭(zhēng)激烈,認(rèn)真對(duì)待每位客戶,為客戶提供賞心悅目的作品。 與客戶共同發(fā)展進(jìn)步,是我們永遠(yuǎn)的責(zé)任!

這篇文章我就帶著大家再來(lái)看看 Dubbo 服務(wù)引入全流程,這篇服務(wù)引入寫(xiě)完下一篇就要來(lái)個(gè)全鏈路打通了,看看大家看完會(huì)不會(huì)有種任督二脈都被打通的感覺(jué)。

在寫(xiě)文章的過(guò)程中丙還發(fā)現(xiàn)官網(wǎng)的一點(diǎn)小問(wèn)題,下文中會(huì)提到。

話不多說(shuō),咱們直接進(jìn)入正題。

服務(wù)引用大致流程

我們已經(jīng)得知 Provider將自己的服務(wù)暴露出來(lái),注冊(cè)到注冊(cè)中心,而 Consumer無(wú)非就是通過(guò)一波操作從注冊(cè)中心得知 Provider 的信息,然后自己封裝一個(gè)調(diào)用類(lèi)和 Provider 進(jìn)行深入地交流。

而之前的文章我都已經(jīng)提到在 Dubbo中一個(gè)可執(zhí)行體就是 Invoker,所有調(diào)用都要向 Invoker 靠攏,因此可以推斷出應(yīng)該要先生成一個(gè) Invoker,然后又因?yàn)榭蚣苄枰磺秩霕I(yè)務(wù)代碼的方向發(fā)展,那我們的 Consumer 需要無(wú)感知的調(diào)用遠(yuǎn)程接口,因此需要搞個(gè)代理類(lèi),包裝一下屏蔽底層的細(xì)節(jié)。

整體大致流程如下:

 

服務(wù)引入的時(shí)機(jī)服務(wù)的引入和服務(wù)的暴露一樣,也是通過(guò) spring 自定義標(biāo)簽機(jī)制解析生成對(duì)應(yīng)的 Bean,Provider Service 對(duì)應(yīng)解析的是 ServiceBean 而 Consumer Reference 對(duì)應(yīng)的是 ReferenceBean。

 

前面服務(wù)暴露的時(shí)機(jī)我們上篇文章分析過(guò)了,在 Spring 容器刷新完成之后開(kāi)始暴露,而服務(wù)的引入時(shí)機(jī)有兩種,第一種是餓漢式,第二種是懶漢式。

餓漢式是通過(guò)實(shí)現(xiàn) Spring 的InitializingBean接口中的 afterPropertiesSet方法,容器通過(guò)調(diào)用 ReferenceBean的 afterPropertiesSet方法時(shí)引入服務(wù)。

懶漢式是只有當(dāng)這個(gè)服務(wù)被注入到其他類(lèi)中時(shí)啟動(dòng)引入流程,也就是說(shuō)用到了才會(huì)開(kāi)始服務(wù)引入。

默認(rèn)情況下,Dubbo 使用懶漢式引入服務(wù),如果需要使用餓漢式,可通過(guò)配置 dubbo:reference 的 init 屬性開(kāi)啟。

我們可以看到 ReferenceBean還實(shí)現(xiàn)了FactoryBean接口,這里有個(gè)關(guān)于 Spring 的面試點(diǎn)我?guī)Т蠹曳治鲆徊ā?/p>

BeanFactory 、FactoryBean、ObjectFactory

就是這三個(gè)玩意,我單獨(dú)拿出來(lái)說(shuō)一下,從字面上來(lái)看其實(shí)可以得知BeanFactory、ObjectFactory是個(gè)工廠而FactoryBean是個(gè) Bean。

BeanFactory 其實(shí)就是 IOC 容器,有多種實(shí)現(xiàn)類(lèi)我就不分析了,簡(jiǎn)單的說(shuō)就是 Spring 里面的 Bean 都?xì)w它管,而FactoryBean也是 Bean 所以說(shuō)也是歸 BeanFactory 管理的。

那 FactoryBean 到底是個(gè)什么 Bean 呢?它其實(shí)就是把你真實(shí)想要的 Bean 封裝了一層,在真正要獲取這個(gè) Bean 的時(shí)候容器會(huì)調(diào)用 FactoryBean#getObject() 方法,而在這個(gè)方法里面你可以進(jìn)行一些復(fù)雜的組裝操作。

這個(gè)方法就封裝了真實(shí)想要的對(duì)象復(fù)雜的創(chuàng)建過(guò)程。

到這里其實(shí)就很清楚了,就是在真實(shí)想要的 Bean 創(chuàng)建比較復(fù)雜的情況下,或者是一些第三方 Bean 難以修改的情形,使用 FactoryBean 封裝了一層,屏蔽了底層創(chuàng)建的細(xì)節(jié),便于 Bean 的使用。

而 ObjectFactory 這個(gè)是用于延遲查找的場(chǎng)景,它就是一個(gè)普通工廠,當(dāng)?shù)玫?ObjectFactory 對(duì)象時(shí),相當(dāng)于 Bean 沒(méi)有被創(chuàng)建,只有當(dāng) getObject() 方法時(shí),才會(huì)觸發(fā) Bean 實(shí)例化等生命周期。

主要用于暫時(shí)性地獲取某個(gè) Bean Holder 對(duì)象,如果過(guò)早的加載,可能會(huì)引起一些意外的情況,比如當(dāng) Bean A 依賴 Bean B 時(shí),如果過(guò)早地初始化 A,那么 B 里面的狀態(tài)可能是中間狀態(tài),這時(shí)候使用 A 容易導(dǎo)致一些錯(cuò)誤。

總結(jié)的說(shuō) BeanFactory 就是 IOC 容器,F(xiàn)actoryBean 是特殊的 Bean, 用來(lái)封裝創(chuàng)建比較復(fù)雜的對(duì)象,而 ObjectFactory 主要用于延遲查找的場(chǎng)景,延遲實(shí)例化對(duì)象。

服務(wù)引入的三種方式

服務(wù)的引入又分為了三種,第一種是本地引入、第二種是直接連接引入遠(yuǎn)程服務(wù)、第三種是通過(guò)注冊(cè)中心引入遠(yuǎn)程服務(wù)。

 

本地引入不知道大家是否還有印象,之前服務(wù)暴露的流程每個(gè)服務(wù)都會(huì)通過(guò)搞一個(gè)本地暴露,走 injvm 協(xié)議(當(dāng)然你要是 scope = remote 就沒(méi)本地引用了),因?yàn)榇嬖谝粋€(gè)服務(wù)端既是 Provider 又是 Consumer 的情況,然后有可能自己會(huì)調(diào)用自己的服務(wù),因此就弄了一個(gè)本地引入,這樣就避免了遠(yuǎn)程網(wǎng)絡(luò)調(diào)用的開(kāi)銷(xiāo)。

所以服務(wù)引入會(huì)先去本地緩存找找看有沒(méi)有本地服務(wù)。

直連遠(yuǎn)程引入服務(wù),這個(gè)其實(shí)就是平日測(cè)試的情況下用用,不需要啟動(dòng)注冊(cè)中心,由 Consumer 直接配置寫(xiě)死 Provider 的地址,然后直連即可。

注冊(cè)中心引入遠(yuǎn)程服務(wù),這個(gè)就是重點(diǎn)了,Consumer 通過(guò)注冊(cè)中心得知 Provider 的相關(guān)信息,然后進(jìn)行服務(wù)的引入,這里還包括多注冊(cè)中心,同一個(gè)服務(wù)多個(gè)提供者的情況,如何抉擇如何封裝,如何進(jìn)行負(fù)載均衡、容錯(cuò)并且讓使用者無(wú)感知,這就是個(gè)技術(shù)活。

本文用的就是單注冊(cè)中心引入遠(yuǎn)程服務(wù),讓我們來(lái)看看 Dubbo 是如何做的吧。

服務(wù)引入流程解析

默認(rèn)是懶漢式的,所以服務(wù)引入的入口就是 ReferenceBean 的 getObject 方法。

 

可以看到很簡(jiǎn)單,就是調(diào)用 get 方法,如果當(dāng)前還沒(méi)有這個(gè)引用那么就執(zhí)行 init 方法。

官網(wǎng)的一個(gè)小問(wèn)題這個(gè)問(wèn)題

就在 if (ref == null) 這一行,其實(shí)是一位老哥在調(diào)試的時(shí)候發(fā)現(xiàn)這個(gè) ref 竟然不等于 null,因此就進(jìn)不到 init 方法里面調(diào)試了,后來(lái)他發(fā)現(xiàn)是因?yàn)?IDEA 為了顯示對(duì)象的信息,會(huì)通過(guò) toString 方法獲取對(duì)象對(duì)應(yīng)的信息。

toString 調(diào)用的是 AbstractConfig#toString,而這個(gè)方法會(huì)通過(guò)反射調(diào)用了 ReferenceBean 的 getObject 方法,觸發(fā)了引入服務(wù)動(dòng)作,所以說(shuō)到斷點(diǎn)的時(shí)候 ref != null。

可以看到是通過(guò)方法名來(lái)進(jìn)行反射調(diào)用的,而 getObject 就是 get 開(kāi)頭的,因此會(huì)被調(diào)用。

所以這個(gè)哥們提了個(gè) PR,但是一開(kāi)始沒(méi)有被接受,一位 Member 認(rèn)為這不是 bug, idea 設(shè)置一下不讓調(diào)用 toString 就好了。

 

不過(guò)另一位 Member 覺(jué)得這個(gè) PR 挺好的,并且 Dubbo 項(xiàng)目二代掌門(mén)人北緯30也發(fā)話了,因此這個(gè) PR 被受理了。

 

至此我們已經(jīng)知道這個(gè)小問(wèn)題了,然后官網(wǎng)上其實(shí)也寫(xiě)的很清楚。

 

但是小問(wèn)題來(lái)了,之前我在文章提到我的源碼版本是 2.6.5,是在 github 的 releases 里面下的,這個(gè) tostring 問(wèn)題其實(shí)我挺早之前就知道了,我想的是我 2.6.5 穩(wěn)的一批,誰(shuí)知道翻車(chē)了。

 

我調(diào)試的時(shí)候也沒(méi)進(jìn)到 init 方法因?yàn)?ref 也沒(méi)等于 null,我就奇怪了,我里面去看了下 toString 方法,2.6.5版本竟然沒(méi)有修改?沒(méi)有將 getObject 做過(guò)濾,因此還是被調(diào)用了。

我又打開(kāi)了2.7.5版本的代碼,發(fā)現(xiàn)是修改過(guò)的判斷。

 

我又去特意下了 2.6.6 版本的代碼,發(fā)現(xiàn)也是修改過(guò)的,因此這個(gè)修改并不是隨著 2.6.5版本發(fā)布,而是 2.6.6,除非我下的是個(gè)假包,這就是我說(shuō)的小問(wèn)題了,不過(guò)影響不大。

其實(shí)提到這一段主要想說(shuō)的是那個(gè) PR,作為一個(gè)開(kāi)源軟件的輸出者,很多細(xì)節(jié)也是很重要的,這個(gè)問(wèn)題其實(shí)很影響源碼的調(diào)試,因?yàn)閷?duì)代碼不熟,肯定會(huì)一臉懵逼,誰(shuí)知道是不是哪個(gè)后臺(tái)線程異步引入了呢。

提這個(gè) PR 的老哥花了兩個(gè)小時(shí)才搞清楚真正的原因,所以說(shuō)雖然這不是個(gè) bug 但是很影響那些想深入了解 Dubbo 內(nèi)部結(jié)構(gòu)的同學(xué)們,這種改配置去適應(yīng)的方案是不可取了,還好最終的方案是改代碼。

好了讓我們回到今天的主題,接下來(lái)分析的就是那個(gè)不讓我進(jìn)去的 init 方法了。

源碼分析

init 方法很長(zhǎng),不過(guò)大部分就是檢查配置然后將配置構(gòu)建成 map ,這一大段我就不分析了,我們直接看一下構(gòu)建完的 map 長(zhǎng)什么樣。

 

然后就進(jìn)入重點(diǎn)方法 createProxy,從名字可以得到就是要?jiǎng)?chuàng)建的一個(gè)代理,因?yàn)榇a很長(zhǎng),我就一段一段的分析。

 

如果是走本地的話,那么直接構(gòu)建個(gè)走本地協(xié)議的 URL 然后進(jìn)行服務(wù)的引入,即 refprotocol.refer,這個(gè)方法之后會(huì)做分析,本地的引入就不深入了,就是去之前服務(wù)暴露的 exporterMap 拿到服務(wù)。

 

如果不是本地,那肯定是遠(yuǎn)程了,接下來(lái)就是判斷是點(diǎn)對(duì)點(diǎn)直連 provider 還是通過(guò)注冊(cè)中心拿到 provider 信息再連接 provider 了,我們分析一下配置了 url 的情況,如果配置了 url 那么不是直連的地址,就是注冊(cè)中心的地址。

 

然后就是沒(méi)配置 url 的情況,到這里肯定走的就是注冊(cè)中心引入遠(yuǎn)程服務(wù)了。

 

最終拼接出來(lái)的 URL 長(zhǎng)這樣。

 

可以看到這一部分其實(shí)就是根據(jù)各種參數(shù)來(lái)組裝 URL ,因?yàn)槲覀兊淖赃m應(yīng)擴(kuò)展都需要根據(jù) URL 的參數(shù)來(lái)進(jìn)行的。

 

至此我先畫(huà)個(gè)圖,給大家先捋一下。

 

這其實(shí)就是整個(gè)流程了,簡(jiǎn)述一下就是先檢查配置,通過(guò)配置構(gòu)建一個(gè) map ,然后利用 map 來(lái)構(gòu)建 URL ,再通過(guò) URL 上的協(xié)議利用自適應(yīng)擴(kuò)展機(jī)制調(diào)用對(duì)應(yīng)的 protocol.refer 得到相應(yīng)的 invoker 。

在有多個(gè) URL 的時(shí)候,先遍歷構(gòu)建出 invoker 然后再由 StaticDirectory 封裝一下,然后通過(guò) cluster 進(jìn)行合并,只暴露出一個(gè) invoker 。

然后再構(gòu)建代理,封裝 invoker 返回服務(wù)引用,之后 Comsumer 調(diào)用的就是這個(gè)代理類(lèi)。

相信通過(guò)圖和上面總結(jié)性的簡(jiǎn)述已經(jīng)知道大致的服務(wù)引入流程了,不過(guò)還是有很多細(xì)節(jié),比如如何從注冊(cè)中心得到 Provider 的地址,invoker 里面到底是怎么樣的?別急,我們繼續(xù)看。

從前面的截圖我們可以看到此時(shí)的協(xié)議是 registry 因此走的是 RegistryProtocol#refer,我們來(lái)看一下這個(gè)方法。

 

主要就是獲取注冊(cè)中心實(shí)例,然后調(diào)用 doRefer 進(jìn)行真正的 refer。

 

這個(gè)方法很關(guān)鍵,可以看到生成了RegistryDirectory 這個(gè) directory 塞了注冊(cè)中心實(shí)例,它自身也實(shí)現(xiàn)了NotifyListener 接口,因此注冊(cè)中心的監(jiān)聽(tīng)其實(shí)是靠這家伙來(lái)處理的。

然后向注冊(cè)中心注冊(cè)自身的信息,并且向注冊(cè)中心訂閱了 providers 節(jié)點(diǎn)、 configurators 節(jié)點(diǎn) 和 routers 節(jié)點(diǎn),訂閱了之后 RegistryDirectory 會(huì)收到這幾個(gè)節(jié)點(diǎn)下的信息,就會(huì)觸發(fā) DubboInvoker 的生成了,即用于遠(yuǎn)程調(diào)用的 Invoker。

然后通過(guò) cluster 再包裝一下得到 Invoker,因此一個(gè)服務(wù)可能有多個(gè)提供者,最終在 ProviderConsumerRegTable 中記錄這些信息,然后返回 Invoker。

所以我們知道Conusmer 是在 RegistryProtocol#refer 中向注冊(cè)中心注冊(cè)自己的信息,并且訂閱 Provider 和配置的一些相關(guān)信息,我們看看訂閱返回的信息是怎樣的。

 

拿到了Provider的信息之后就可以通過(guò)監(jiān)聽(tīng)觸發(fā) DubboProtocol# refer 了(具體調(diào)用哪個(gè) protocol 還是得看 URL的協(xié)議的,我們這里是 dubbo 協(xié)議),整個(gè)觸發(fā)流程我就不一一跟一下了,看下調(diào)用棧就清楚了。

 

終于我們從注冊(cè)中心拿到遠(yuǎn)程Provider 的信息了,然后進(jìn)行服務(wù)的引入。

 

這里的重點(diǎn)在 getClients,因?yàn)榻K究是要跟遠(yuǎn)程服務(wù)進(jìn)行網(wǎng)絡(luò)調(diào)用的,而 getClients 就是用于獲取客戶端實(shí)例,實(shí)例類(lèi)型為 ExchangeClient,底層依賴 Netty 來(lái)進(jìn)行網(wǎng)絡(luò)通信,并且可以看到默認(rèn)是共享連接。

 

getSharedClient 我就不分析了,就是通過(guò)遠(yuǎn)程地址找 client ,這個(gè) client 還有引用計(jì)數(shù)的功能,如果該遠(yuǎn)程地址還沒(méi)有 client 則調(diào)用 initClient,我們就來(lái)看一下 initClient 方法。

 

而這個(gè)connect最終返回 HeaderExchangeClient里面封裝的是 NettyClient 。

 

然后最終得到的 Invoker就是這個(gè)樣子,可以看到記錄的很多信息,基本上該有的都有了,我這里走的是對(duì)應(yīng)的服務(wù)只有一個(gè) url 的情況,多個(gè) url 無(wú)非也是利用 directory和 cluster再封裝一層。

 

最終將調(diào)用 return (T) proxyFactory.getProxy(invoker); 返回一個(gè)代理對(duì)象,這個(gè)就不做分析了。

到這里,整個(gè)流程就是分析完了,不知道大家清晰了沒(méi)?我再補(bǔ)充前面的圖,來(lái)一個(gè)完整的流程給大家再過(guò)一遍。

 

小結(jié)相信分析下來(lái)整個(gè)流程不難的,總結(jié)地說(shuō)無(wú)非就是通過(guò)配置組成 URL ,然后通過(guò)自適應(yīng)得到對(duì)于的實(shí)現(xiàn)類(lèi)進(jìn)行服務(wù)引入,如果是注冊(cè)中心那么會(huì)向注冊(cè)中心注冊(cè)自己的信息,然后訂閱注冊(cè)中心相關(guān)信息,得到遠(yuǎn)程 provider的 ip 等信息,再通過(guò)netty客戶端進(jìn)行連接。

并且通過(guò)directory 和 cluster 進(jìn)行底層多個(gè)服務(wù)提供者的屏蔽、容錯(cuò)和負(fù)載均衡等,這個(gè)之后文章會(huì)詳細(xì)分析,最終得到封裝好的 invoker再通過(guò)動(dòng)態(tài)代理封裝得到代理類(lèi),讓接口調(diào)用者無(wú)感知的調(diào)用方法。

最后今天這篇文章看下來(lái)相信大家對(duì)服務(wù)的引入應(yīng)該有了清晰的認(rèn)識(shí),其實(shí)里面還是很多細(xì)節(jié)我沒(méi)有展開(kāi)分析,比如一些過(guò)濾鏈的組裝,這其實(shí)在服務(wù)暴露的文章里面已經(jīng)說(shuō)了,同樣服務(wù)引用也有過(guò)濾鏈,不過(guò)篇幅有限就不展開(kāi)了,抓住主線要緊。

至此我已經(jīng)帶大家先過(guò)了一遍 Dubbo 的整體概念和大致流程,介紹了 Dubbo SPI機(jī)制,并且分析了服務(wù)的暴露流程和服務(wù)引入流程,具體的細(xì)節(jié)還是得大家自己去摸索,大致的流程我都講的差不多了。

dubbo系列也快接近尾聲了,雖然我知道每次寫(xiě)硬核技術(shù)看的小伙伴就少了很多,但是還是想寫(xiě)完這個(gè)系列,感謝大家的支持。


標(biāo)題名稱(chēng):女朋友問(wèn)我:Dubbo的服務(wù)引用過(guò)程
文章鏈接:http://www.dlmjj.cn/article/dpjhodo.html