Dubbo服務(wù)發(fā)現(xiàn)、引用過程
本文轉(zhuǎn)載自微信公眾號(hào)「大魚仙人」,作者大魚仙人。轉(zhuǎn)載本文請(qǐng)聯(lián)系大魚仙人公眾號(hào)。
一.前言
前面兩篇分別介紹了Dubbo的入門和Dubbo的服務(wù)暴露
- Dubbo起飛
- 面試官問我:解釋一下Dubbo服務(wù)暴露
這篇我們要說的服務(wù)引用,服務(wù)引用是有兩種情況的,也可以看做是兩種時(shí)機(jī),第一個(gè)是在Spring容器調(diào)用ReferenceBean的afterPropertiesSet方法時(shí)引用服務(wù),第二個(gè)就是在ReferenceBean對(duì)應(yīng)的服務(wù)被注入到其他類中時(shí)引用。這兩個(gè)引用服務(wù)的時(shí)機(jī)區(qū)別在于,第一個(gè)是餓漢式的,第二個(gè)是懶漢式的
是不是一說餓漢和懶漢,大家順便回憶了一波單例模式
默認(rèn)情況下,Dubbo使用懶漢式引用服務(wù)。如果需要使用餓漢式,可通過配置
服務(wù)引入的三種方式:
第一種是引用本地 (JVM) 服務(wù),上篇在服務(wù)暴露里面也說過了每個(gè)服務(wù)都會(huì)通過走injvm協(xié)議然后走本地的暴露,因?yàn)榇嬖谝粋€(gè)服務(wù)端和消費(fèi)端是同一臺(tái)機(jī)器上的情況,這樣就直接走本地調(diào)用了,不需要走遠(yuǎn)程調(diào)用了,節(jié)省網(wǎng)絡(luò)開銷
第二是通過直連方式引用遠(yuǎn)程服務(wù),這種在線上基本不會(huì)采用這種形式的,一般都是平時(shí)我們自己測(cè)試用,直接寫死服務(wù)端的地址來調(diào)用
第三是通過注冊(cè)中心引用遠(yuǎn)程服務(wù),Consumer 通過注冊(cè)中心得知 Provider 的相關(guān)信息,然后進(jìn)行服務(wù)的引入
不管是哪種引用方式,最后都會(huì)得到一個(gè) Invoker 實(shí)例。如果有多個(gè)注冊(cè)中心,多個(gè)服務(wù)提供者,這個(gè)時(shí)候會(huì)得到一組 Invoker 實(shí)例,此時(shí)需要通過集群管理類 Cluster 將多個(gè) Invoker 合并成一個(gè)實(shí)例。合并后的 Invoker 實(shí)例已經(jīng)具備調(diào)用本地或遠(yuǎn)程服務(wù)的能力了
但是呢,開發(fā)者秉承不對(duì)用戶業(yè)務(wù)代碼侵入的原則,所以此時(shí)框架還需要通過代理工廠類ProxyFactory為服務(wù)接口生成代理類,并讓代理類去調(diào)用Invoker邏輯,避免了Dubbo框架代碼對(duì)業(yè)務(wù)代碼的侵入
二.服務(wù)發(fā)現(xiàn)
dubbo的服務(wù)發(fā)現(xiàn),就是通過從注冊(cè)中心訂閱服務(wù)提供者,并且組裝成URL,然后通過URL創(chuàng)建出invoker來實(shí)現(xiàn)的
服務(wù)的引入和服務(wù)的暴露一樣,也是通過 spring 自定義標(biāo)簽機(jī)制解析生成對(duì)應(yīng)的 Bean,Provider Service 對(duì)應(yīng)解析的是 ServiceBean 而 Consumer Reference 對(duì)應(yīng)的是 ReferenceBean
dubbo 的服務(wù)發(fā)現(xiàn),是通過從注冊(cè)中心訂閱服務(wù)提供者組裝成 URL,然后通過 URL 創(chuàng)建出 Invoker 來實(shí)現(xiàn)的。
這里是入口,進(jìn)去看ReferenceBean
createLazyProxy中我們會(huì)看到DubboReferenceLazyInitTargetSource這一目標(biāo)資源,點(diǎn)進(jìn)來new的地方,里面的protected的方法createObject調(diào)用了getCallProxy,而這個(gè)方法最終調(diào)用的是referenceConfig.get()
點(diǎn)進(jìn)來,我們進(jìn)入的是ReferenceConfig類的方法,而非ReferenceBean的,這是因?yàn)镽eferenceConfig是作為ReferenceBean的內(nèi)部的屬性出現(xiàn)的
Init()方法內(nèi)部主要就是通過Map設(shè)置各種參數(shù),我們看init其中的一個(gè)方法叫做createProxy,我們根據(jù)名字也可以知道大概意思就是創(chuàng)建代理對(duì)象,點(diǎn)進(jìn)去看看
如果是走本地的話,那么直接構(gòu)建個(gè)本地協(xié)議的 URL 然后進(jìn)行服務(wù)的引入,即 refprotocol.refer,這個(gè)方法之后會(huì)做分析,本地的引入就不深入了,就是去之前服務(wù)暴露的 exporterMap 拿到服務(wù)
如果不是本地,那肯定是遠(yuǎn)程了,接下來就是判斷是點(diǎn)對(duì)點(diǎn)直連 provider 還是通過注冊(cè)中心拿到 provider 信息再連接 provider 了,我們分析一下配置了 url 的情況,如果配置了 url 那么不是直連的地址,就是注冊(cè)中心的地址
這其實(shí)就是整個(gè)流程了,簡述一下就是先檢查配置,通過配置構(gòu)建一個(gè) map ,然后利用 map 來構(gòu)建 URL ,再通過 URL 上的協(xié)議利用自適應(yīng)擴(kuò)展機(jī)制調(diào)用對(duì)應(yīng)的 protocol.refer 得到相應(yīng)的 invoker
我給大家總結(jié)個(gè)流程圖,這樣大家看著更加清晰
三.服務(wù)引用
Dubbo 的服務(wù)引用,實(shí)際上是為引用的接口創(chuàng)建一個(gè) Proxy,這個(gè) Proxy 的功能就是去執(zhí)行 refprotocol.refer(interfaceClass, url) 創(chuàng)建出來的 Invoker。當(dāng)服務(wù)提供者有多個(gè)時(shí),就創(chuàng)建一個(gè) ClusterInvoker。
Cluster 是一個(gè) SPI 擴(kuò)展點(diǎn),默認(rèn)使用com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
所以,Consumer 端服務(wù)調(diào)用的邏輯被封裝在 refprotocol.refer(interfaceClass, url) 創(chuàng)建出來的 Invoker 上
主要就是獲取注冊(cè)中心實(shí)例,然后調(diào)用 doRefer 進(jìn)行真正的 refer。
這里會(huì)向注冊(cè)中心注冊(cè)自身的信息,生成一個(gè)Invoker,底層生成用于遠(yuǎn)程調(diào)用的invoker,然后通過cluster包裝一下再得到ClusterInvoker,因此一個(gè)服務(wù)可能有多個(gè)提供者,然后最后注冊(cè)相應(yīng)的監(jiān)聽器
拿到了Provider的信息之后就可以通過監(jiān)聽觸發(fā) Protocol# refer 了,具體調(diào)用哪個(gè) protocol 還是得看 URL的協(xié)議的,我們看下這個(gè)內(nèi)部DubboProtocol的refer
而這個(gè)connect最終返回 HeaderExchangeClient里面封裝的是 NettyClient,然后最終得到的invoker就是對(duì)這個(gè)client的封裝,最終將返回一個(gè)Proxy的代理對(duì)象
四.回顧
其實(shí)整個(gè)流程看代碼啥的,一開始可能會(huì)遇到很多新名詞,但是細(xì)細(xì)一想其實(shí)不難,靜下心來好好分析,就都很簡單了
其實(shí)就是通過各種配置參數(shù)和協(xié)議組裝成相應(yīng)的URL,然后通過自動(dòng)適配去對(duì)相應(yīng)的實(shí)現(xiàn)類進(jìn)行相應(yīng)的服務(wù)的引入和后續(xù)的調(diào)用
如果是寫死的地址就直接連接,是注冊(cè)中心就向注冊(cè)中心注冊(cè)信息,然后訂閱注冊(cè)中心的相關(guān)信息,得到服務(wù)提供者的IP端口號(hào)等信息,通過netty進(jìn)行連接,底層會(huì)通過directory和cluster進(jìn)行底層多個(gè)服務(wù)的屏蔽和負(fù)載均衡的處理,得到代理對(duì)象Invoker,再通過動(dòng)態(tài)代理封裝得到代理類,總之就是不侵入業(yè)務(wù)代碼,能用代理解決的就用代理
不侵入代碼的最好辦法就是加代理,遇事不決加層代理