初始化過程細(xì)節(jié)
解析服務(wù)
基于 dubbo.jar 內(nèi)的 META-INF/spring.handlers
配置,Spring 在遇到 dubbo 名稱空間時(shí)昌妹,會(huì)回調(diào) DubboNamespaceHandler
。
所有 dubbo 的標(biāo)簽握截,都統(tǒng)一用 DubboBeanDefinitionParser
進(jìn)行解析飞崖,基于一對(duì)一屬性映射,將 XML 標(biāo)簽解析為 Bean 對(duì)象谨胞。
在 ServiceConfig.export()
或 ReferenceConfig.get()
初始化時(shí)固歪,將 Bean 對(duì)象轉(zhuǎn)換 URL 格式,所有 Bean 屬性轉(zhuǎn)成 URL 的參數(shù)胯努。
然后將 URL 傳給 協(xié)議擴(kuò)展點(diǎn)牢裳,基于擴(kuò)展點(diǎn)的 擴(kuò)展點(diǎn)自適應(yīng)機(jī)制逢防,根據(jù) URL 的協(xié)議頭,進(jìn)行不同協(xié)議的服務(wù)暴露或引用蒲讯。
暴露服務(wù)
1. 只暴露服務(wù)端口:
在沒有注冊(cè)中心忘朝,直接暴露提供者的情況下 [1],ServiceConfig
解析出的 URL 的格式為:dubbo://service-host/com.foo.FooService?version=1.0.0
伶椿。
基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制辜伟,通過 URL 的 dubbo://
協(xié)議頭識(shí)別氓侧,直接調(diào)用 DubboProtocol
的 export()
方法脊另,打開服務(wù)端口。
2. 向注冊(cè)中心暴露服務(wù):
在有注冊(cè)中心约巷,需要注冊(cè)提供者地址的情況下 [2]偎痛,ServiceConfig
解析出的 URL 的格式為: registry://registry-host/org.apache.dubbo.registry.RegistryService?export=URL.encode("dubbo://service-host/com.foo.FooService?version=1.0.0")
,
基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制独郎,通過 URL 的 registry://
協(xié)議頭識(shí)別踩麦,就會(huì)調(diào)用 RegistryProtocol
的 export()
方法,將 export
參數(shù)中的提供者 URL氓癌,先注冊(cè)到注冊(cè)中心谓谦。
再重新傳給 Protocol
擴(kuò)展點(diǎn)進(jìn)行暴露: dubbo://service-host/com.foo.FooService?version=1.0.0
,然后基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制贪婉,通過提供者 URL 的 dubbo://
協(xié)議頭識(shí)別反粥,就會(huì)調(diào)用 DubboProtocol
的 export()
方法,打開服務(wù)端口疲迂。
引用服務(wù)
1. 直連引用服務(wù):
在沒有注冊(cè)中心才顿,直連提供者的情況下 [3],ReferenceConfig
解析出的 URL 的格式為:dubbo://service-host/com.foo.FooService?version=1.0.0
尤蒿。
基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制郑气,通過 URL 的 dubbo://
協(xié)議頭識(shí)別,直接調(diào)用 DubboProtocol
的 refer()
方法腰池,返回提供者引用尾组。
2. 從注冊(cè)中心發(fā)現(xiàn)引用服務(wù):
在有注冊(cè)中心,通過注冊(cè)中心發(fā)現(xiàn)提供者地址的情況下 [4]示弓,ReferenceConfig
解析出的 URL 的格式為:registry://registry-host/org.apache.dubbo.registry.RegistryService?refer=URL.encode("consumer://consumer-host/com.foo.FooService?version=1.0.0")
讳侨。
基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制,通過 URL 的 registry://
協(xié)議頭識(shí)別避乏,就會(huì)調(diào)用 RegistryProtocol
的 refer()
方法爷耀,基于 refer
參數(shù)中的條件,查詢提供者 URL拍皮,如: dubbo://service-host/com.foo.FooService?version=1.0.0
歹叮。
基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制跑杭,通過提供者 URL 的 dubbo://
協(xié)議頭識(shí)別,就會(huì)調(diào)用 DubboProtocol
的 refer()
方法咆耿,得到提供者引用德谅。
然后 RegistryProtocol
將多個(gè)提供者引用,通過 Cluster
擴(kuò)展點(diǎn)萨螺,偽裝成單個(gè)提供者引用返回窄做。
攔截服務(wù)
基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制,所有的 Protocol
擴(kuò)展點(diǎn)都會(huì)自動(dòng)套上 Wrapper
類慰技。
基于 ProtocolFilterWrapper
類椭盏,將所有 Filter
組裝成鏈,在鏈的最后一節(jié)調(diào)用真實(shí)的引用吻商。
基于 ProtocolListenerWrapper
類掏颊,將所有 InvokerListener
和 ExporterListener
組裝集合,在暴露和引用前后艾帐,進(jìn)行回調(diào)乌叶。
包括監(jiān)控在內(nèi),所有附加功能柒爸,全部通過 Filter
攔截實(shí)現(xiàn)准浴。
遠(yuǎn)程調(diào)用細(xì)節(jié)
服務(wù)提供者暴露一個(gè)服務(wù)的詳細(xì)過程
上圖是服務(wù)提供者暴露服務(wù)的主過程:
首先 ServiceConfig
類拿到對(duì)外提供服務(wù)的實(shí)際類 ref(如:HelloWorldImpl),然后通過 ProxyFactory
類的 getInvoker
方法使用 ref 生成一個(gè) AbstractProxyInvoker
實(shí)例,到這一步就完成具體服務(wù)到 Invoker
的轉(zhuǎn)化捎稚。接下來就是 Invoker
轉(zhuǎn)換到 Exporter
的過程乐横。
Dubbo 處理服務(wù)暴露的關(guān)鍵就在 Invoker
轉(zhuǎn)換到 Exporter
的過程,上圖中的紅色部分阳藻。下面我們以 Dubbo 和 RMI 這兩種典型協(xié)議的實(shí)現(xiàn)來進(jìn)行說明:
Dubbo 的實(shí)現(xiàn)
Dubbo 協(xié)議的 Invoker
轉(zhuǎn)為 Exporter
發(fā)生在 DubboProtocol
類的 export
方法晰奖,它主要是打開 socket 偵聽服務(wù),并接收客戶端發(fā)來的各種請(qǐng)求腥泥,通訊細(xì)節(jié)由 Dubbo 自己實(shí)現(xiàn)匾南。
RMI 的實(shí)現(xiàn)
RMI 協(xié)議的 Invoker
轉(zhuǎn)為 Exporter
發(fā)生在 RmiProtocol
類的 export
方法,它通過 Spring 或 Dubbo 或 JDK 來實(shí)現(xiàn) RMI 服務(wù)蛔外,通訊細(xì)節(jié)這一塊由 JDK 底層來實(shí)現(xiàn)蛆楞,這就省了不少工作量。
服務(wù)消費(fèi)者消費(fèi)一個(gè)服務(wù)的詳細(xì)過程
上圖是服務(wù)消費(fèi)的主過程:
首先 ReferenceConfig
類的 init
方法調(diào)用 Protocol
的 refer
方法生成 Invoker
實(shí)例(如上圖中的紅色部分)夹厌,這是服務(wù)消費(fèi)的關(guān)鍵豹爹。接下來把 Invoker
轉(zhuǎn)換為客戶端需要的接口(如:HelloWorld)。
關(guān)于每種協(xié)議如 RMI/Dubbo/Web service 等它們?cè)谡{(diào)用 refer
方法生成 Invoker
實(shí)例的細(xì)節(jié)和上一章節(jié)所描述的類似矛纹。
滿眼都是 Invoker
由于 Invoker
是 Dubbo 領(lǐng)域模型中非常重要的一個(gè)概念臂聋,很多設(shè)計(jì)思路都是向它靠攏。這就使得 Invoker
滲透在整個(gè)實(shí)現(xiàn)代碼里,對(duì)于剛開始接觸 Dubbo 的人孩等,確實(shí)容易給搞混了艾君。 下面我們用一個(gè)精簡(jiǎn)的圖來說明最重要的兩種 Invoker
:服務(wù)提供 Invoker
和服務(wù)消費(fèi) Invoker
:
為了更好的解釋上面這張圖,我們結(jié)合服務(wù)消費(fèi)和提供者的代碼示例來進(jìn)行說明:
服務(wù)消費(fèi)者代碼:
public class DemoClientAction {
private DemoService demoService;
public void setDemoService(DemoService demoService) {
this.demoService = demoService;
}
public void start() {
String hello = demoService.sayHello("world" + i);
}
}
上面代碼中的 DemoService
就是上圖中服務(wù)消費(fèi)端的 proxy肄方,用戶代碼通過這個(gè) proxy 調(diào)用其對(duì)應(yīng)的 Invoker
[5]冰垄,而該 Invoker
實(shí)現(xiàn)了真正的遠(yuǎn)程服務(wù)調(diào)用。
服務(wù)提供者代碼:
public class DemoServiceImpl implements DemoService {
public String sayHello(String name) throws RemoteException {
return "Hello " + name;
}
}
上面這個(gè)類會(huì)被封裝成為一個(gè) AbstractProxyInvoker
實(shí)例权她,并新生成一個(gè) Exporter
實(shí)例虹茶。這樣當(dāng)網(wǎng)絡(luò)通訊層收到一個(gè)請(qǐng)求后,會(huì)找到對(duì)應(yīng)的 Exporter
實(shí)例隅要,并調(diào)用它所對(duì)應(yīng)的 AbstractProxyInvoker
實(shí)例蝴罪,從而真正調(diào)用了服務(wù)提供者的代碼。Dubbo 里還有一些其他的 Invoker
類拾徙,但上面兩種是最重要的洲炊。
擴(kuò)展說明
RPC 協(xié)議擴(kuò)展感局,封裝遠(yuǎn)程調(diào)用細(xì)節(jié)尼啡。
契約:
當(dāng)用戶調(diào)用
refer()
所返回的Invoker
對(duì)象的invoke()
方法時(shí),協(xié)議需相應(yīng)執(zhí)行同 URL 遠(yuǎn)端export()
傳入的Invoker
對(duì)象的invoke()
方法询微。其中崖瞭,
refer()
返回的Invoker
由協(xié)議實(shí)現(xiàn),協(xié)議通常需要在此Invoker
中發(fā)送遠(yuǎn)程請(qǐng)求撑毛,export()
傳入的Invoker
由框架實(shí)現(xiàn)并傳入书聚,協(xié)議不需要關(guān)心。
注意:
協(xié)議不關(guān)心業(yè)務(wù)接口的透明代理藻雌,以
Invoker
為中心雌续,由外層將Invoker
轉(zhuǎn)換為業(yè)務(wù)接口。協(xié)議不一定要是 TCP 網(wǎng)絡(luò)通訊胯杭,比如通過共享文件驯杜,IPC 進(jìn)程間通訊等。