轉(zhuǎn)載自 http://dubbo.apache.org/books/dubbo-dev-book/implementation.html
實(shí)現(xiàn)細(xì)節(jié)
初始化過程細(xì)節(jié)
解析服務(wù)
基于 dubbo.jar 內(nèi)的META-INF/spring.handlers配置,Spring 在遇到 dubbo 名稱空間時(shí),會回調(diào)DubboNamespaceHandler丐谋。
所有 dubbo 的標(biāo)簽芍碧,都統(tǒng)一用DubboBeanDefinitionParser進(jìn)行解析,基于一對一屬性映射号俐,將 XML 標(biāo)簽解析為 Bean 對象泌豆。
在ServiceConfig.export()或ReferenceConfig.get()初始化時(shí),將 Bean 對象轉(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ù)端口:
在沒有注冊中心笨忌,直接暴露提供者的情況下1蓝仲,ServiceConfig解析出的 URL 的格式為:dubbo://service-host/com.foo.FooService?version=1.0.0。
基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制官疲,通過 URL 的dubbo://協(xié)議頭識別袱结,直接調(diào)用DubboProtocol的export()方法,打開服務(wù)端口途凫。
2. 向注冊中心暴露服務(wù):
在有注冊中心垢夹,需要注冊提供者地址的情況下2,ServiceConfig解析出的 URL 的格式為:
registry://registry-host/com.alibaba.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é)議頭識別,就會調(diào)用RegistryProtocol的export()方法掩完,將export參數(shù)中的提供者 URL噪漾,先注冊到注冊中心。
再重新傳給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é)議頭識別,就會調(diào)用DubboProtocol的export()方法恶阴,打開服務(wù)端口诈胜。
引用服務(wù)
1. 直連引用服務(wù):
在沒有注冊中心,直連提供者的情況下3冯事,ReferenceConfig解析出的 URL 的格式為:
dubbo://service-host/com.foo.FooService?version=1.0.0焦匈。
基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制,通過 URL 的dubbo://協(xié)議頭識別昵仅,直接調(diào)用DubboProtocol的refer()方法缓熟,返回提供者引用累魔。
2. 從注冊中心發(fā)現(xiàn)引用服務(wù):
在有注冊中心,通過注冊中心發(fā)現(xiàn)提供者地址的情況下4够滑,ReferenceConfig解析出的 URL 的格式為:
registry://registry-host/com.alibaba.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é)議頭識別彰触,就會調(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é)議頭識別俭茧,就會調(diào)用DubboProtocol的refer()方法咆疗,得到提供者引用。
然后RegistryProtocol將多個(gè)提供者引用母债,通過Cluster擴(kuò)展點(diǎn)午磁,偽裝成單個(gè)提供者引用返回。
攔截服務(wù)
基于擴(kuò)展點(diǎn)自適應(yīng)機(jī)制毡们,所有的Protocol擴(kuò)展點(diǎn)都會自動套上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類拿到對外提供服務(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ā)來的各種請求拿撩,通訊細(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 等它們在調(diào)用refer方法生成Invoker實(shí)例的細(xì)節(jié)和上一章節(jié)所描述的類似期吓。
滿眼都是 Invoker
由于Invoker是 Dubbo 領(lǐng)域模型中非常重要的一個(gè)概念,很多設(shè)計(jì)思路都是向它靠攏倾芝。這就使得Invoker滲透在整個(gè)實(shí)現(xiàn)代碼里讨勤,對于剛開始接觸 Dubbo 的人,確實(shí)容易給搞混了晨另。下面我們用一個(gè)精簡的圖來說明最重要的兩種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)用其對應(yīng)的Invoker5借尿,而該Invoker實(shí)現(xiàn)了真正的遠(yuǎn)程服務(wù)調(diào)用刨晴。
服務(wù)提供者代碼:
public class DemoService Implimplements?? DemoService{
?????????? public String sayHello(String name)throws RemoteException{
?????????????????? return"Hello "+ name;? ?
??????????? }
}
上面這個(gè)類會被封裝成為一個(gè)AbstractProxyInvoker實(shí)例,并新生成一個(gè)Exporter實(shí)例路翻。這樣當(dāng)網(wǎng)絡(luò)通訊層收到一個(gè)請求后狈癞,會找到對應(yīng)的Exporter實(shí)例,并調(diào)用它所對應(yīng)的AbstractProxyInvoker實(shí)例茂契,從而真正調(diào)用了服務(wù)提供者的代碼蝶桶。Dubbo 里還有一些其他的Invoker類,但上面兩種是最重要的掉冶。
遠(yuǎn)程通訊細(xì)節(jié)
協(xié)議頭約定
線程派發(fā)模型
Dispather:all,direct,message,execution,connection
ThreadPool:fixed,cached
? ? ? ? ? ? ? ?