dubbo源碼分析23 -- provider 接收與發(fā)送原理

在前面一篇博客中分享了 dubbo 在網(wǎng)絡(luò)通信當(dāng)中的 consumer 的發(fā)送以及接收原理庵朝。通過(guò)集群容錯(cuò)最終選擇一個(gè)合適的 Invoke 通過(guò) netty 直聯(lián)調(diào)用 provider 的服務(wù)。眾所周知衫仑, netty 是基于 Java Nio 的 Reactor 模型的異步網(wǎng)絡(luò)通信框架,所以 dubbo 在 consumer 端把異步變成了同步髓梅。

大概總結(jié)了 consumer 的發(fā)送與接收原理褒脯,下面我們來(lái)討論一下 dubbo 網(wǎng)絡(luò)通信當(dāng)中 provider 的接收與發(fā)送原理。這樣就完成了 dubbo 架構(gòu)圖里面的 consumer 調(diào)用 provider 的過(guò)程.

dubbo-architecture.png

本次是分析 dubbo 的 provider 的接收與發(fā)送原理,討論包括以下幾個(gè)點(diǎn):

  • provider 接收 consumer 請(qǐng)求
  • provider 的擴(kuò)展點(diǎn)調(diào)用
  • provider 響應(yīng) consumer 調(diào)用
  • dubbo 服務(wù)調(diào)用總結(jié)

1岛蚤、provider 接收 consumer 請(qǐng)求

同 consumer 一樣 provider 默認(rèn)也是通過(guò) netty 進(jìn)行網(wǎng)絡(luò)通信的邑狸。在之前的分析 dubbo 進(jìn)行服務(wù)暴露(NettyServer#doOpen)的時(shí)候, 它是通過(guò) Netty 進(jìn)行服務(wù)暴露灭美,添加了一個(gè) dubbo 的自定義 netty 的 ChannelHandler 也就是 NettyServerHandler 來(lái)處理網(wǎng)絡(luò)通信事件推溃。下面我們來(lái)看一下 provider 是如何接收 consumer 發(fā)送過(guò)來(lái)請(qǐng)求的。

NettyServerHandler.jpg

以上就是 provider 接收 consumer 端的調(diào)用圖届腐,可以發(fā)現(xiàn)其實(shí)是和 consumer 端接收 provider 端的類似都是通過(guò)自定義 netty 的 ChannelHandler 也就是 NettyServerHandler 來(lái)接收網(wǎng)絡(luò)請(qǐng)求铁坎。并且同樣的通過(guò) dubbo 自定義的 ChannelHandler 來(lái)處理請(qǐng)求。下面我們還是來(lái)分析一下這些 dubbo 自定義 ChannelHandler 的作用:

  • MultiMessageHandler:支持 MultiMessage 消息處理犁苏,也就是多條消息處理硬萍。
  • HeartbeatHandler:netty 心條檢測(cè)。如果心跳請(qǐng)求围详,發(fā)送心跳然后直接 return朴乖,如果是心跳響應(yīng)直接 return。
  • AllChannelHandler:使用線程池通過(guò) ChannelEventRunnable 工作類來(lái)處理網(wǎng)絡(luò)事件助赞。
  • DecodeHandler:解碼 message买羞,解析成 dubbo 中的 Request 對(duì)象
  • HeaderExchangeHandler:處理解析后的 consumer 端請(qǐng)求的 Request 信息,把請(qǐng)求信息傳遞到 DubboProtocol 并從 DubboExpoter 里面找到相應(yīng)具體的 Invoke 進(jìn)行服務(wù)調(diào)用(后面具體分析)雹食。

其實(shí)可以看到 consumer 與 provider 接收網(wǎng)絡(luò)請(qǐng)求都是通過(guò)自定義 netty 的 ChannelHandler畜普。然后通過(guò)調(diào)用自定義 ChannelHandler#channelRead (其實(shí)是 ChannelHandler 的子接口 ChannelInboundHandler#channelRead )來(lái)接收并處理網(wǎng)絡(luò)請(qǐng)求。在之前服務(wù)暴露分析的時(shí)候我們講過(guò)AbstractProtocol#exporterMap 也就是 dubbo 在進(jìn)行服務(wù)暴露的時(shí)候通過(guò) AbstractProtocol#serviceKey 為 key 以 DubboExporter(Invoke 轉(zhuǎn)化成 Exporter) 為 value 的服務(wù)接口暴露信息群叶。然后把請(qǐng)求信息交給 DubboProtocol 根據(jù) consumer 里面 Request 里面的 Invocation 請(qǐng)求信息獲取到 DubboExporter吃挑。最后通過(guò)DubboExporter#getInvoker 獲取暴露服務(wù)具體的服務(wù)實(shí)現(xiàn),完成整個(gè)調(diào)用街立。

我們可以看到 consumer 與 provider 進(jìn)行網(wǎng)絡(luò)接收信息是類似的舶衬,相同點(diǎn)都是通過(guò)自定義的 ChannelHandler 來(lái)處理網(wǎng)絡(luò)請(qǐng)求信息。通過(guò) dubbo 這個(gè)自定義的 ChannelHandler 來(lái)適配不同的 Java Nio 框架赎离,因?yàn)樵?AbstractPeer 類中都持有 dubbo 自定義的這個(gè) ChannelHandler 逛犹。 dubbo 默認(rèn)使用的是 netty 作為 Nio 框架,通過(guò)配置 dubbo 還可以以 Mina 與 Grizzly 作為 Nio 框架梁剔。

這個(gè)就用到了 dubbo 的核心 SPI 平等的對(duì)待第三方框架圾浅。

上面我們討論了相同點(diǎn),下面我們來(lái)看一下 consumer 與 provider 接收網(wǎng)絡(luò)請(qǐng)求的不同點(diǎn):

  • consumer 接收的是 provider 端發(fā)送過(guò)來(lái)的 Response(響應(yīng)信息)憾朴,而 provider 是接收 consumer 端發(fā)送過(guò)來(lái)的 Request(請(qǐng)求信息)狸捕。
  • consumer 最后在 HeaderExchangeHandler 中調(diào)用 handleResponse 方法,而 provider 最在是在HeaderExchangeHandler 中調(diào)用 handleRequest 方法众雷。
  • consumer 會(huì)默認(rèn)會(huì)同步等待 provider 處理后的響應(yīng)信息(也可以異步處理)灸拍,而 provider 在處理完成之后就會(huì)同步的把響應(yīng)請(qǐng)求發(fā)送給 consumer.

2做祝、provider 的擴(kuò)展點(diǎn)調(diào)用

與 consumer 引用服務(wù)一樣, provider 在暴露服務(wù)的時(shí)候也會(huì)有擴(kuò)展點(diǎn)鸡岗。 就像 J2EE 調(diào)用 Servlet 的時(shí)候也可以通過(guò) java.servlet.Filter 進(jìn)行調(diào)用擴(kuò)展混槐,dubbo 在進(jìn)行服務(wù)暴露方的時(shí)候也會(huì)有 dubbo 自己的 Filter 擴(kuò)展。那么我們就來(lái)看一下在進(jìn)行 Invoke 調(diào)用的時(shí)候 dubbo 都有哪些擴(kuò)展:

DubboProtocol.jpg

可以看到默認(rèn)情況下轩性,dubbo 在進(jìn)行服務(wù)暴露的時(shí)候會(huì)加上框架自定義的 7 個(gè) Filter 擴(kuò)展声登。下面就來(lái)簡(jiǎn)單描述一下這 7 個(gè) Filter 的作用:

  • EchoFilter:回聲測(cè)試,用于檢測(cè)服務(wù)是否可用,回聲測(cè)試按照正常請(qǐng)求流程執(zhí)行揣苏,能夠測(cè)試整個(gè)調(diào)用是否通暢悯嗓,可用于監(jiān)控。
  • ClassLoaderFilter:
  • GenericFilter:實(shí)現(xiàn)泛化調(diào)用,泛接口實(shí)現(xiàn)方式主要用于服務(wù)器端沒(méi)有API接口及模型類元的情況卸察,參數(shù)及返回值中的所有POJO均用Map表示脯厨,通常用于框架集成.比如:實(shí)現(xiàn)一個(gè)通用的遠(yuǎn)程服務(wù)Mock框架,可通過(guò)實(shí)現(xiàn)GenericService接口處理所有服務(wù)請(qǐng)求坑质。
  • TraceFilter:方法調(diào)用時(shí)間查探擴(kuò)展器, 通過(guò) TraceFilter#addTracer 添加需要查探類的方法與查探最大次數(shù)合武。當(dāng)進(jìn)行方法調(diào)用的時(shí)如果該方法的調(diào)用次數(shù)少于傳遞的最大次數(shù)就會(huì)把方法調(diào)用耗時(shí)發(fā)送給遠(yuǎn)程服務(wù)。
  • MonitorFilter:MonitorFilter 其實(shí)是在分析之前 dubbo monitor 的時(shí)候就進(jìn)行了詳細(xì)的分析涡扼。它主要是通過(guò)<dubbo:monitor protocol="registry" />來(lái)激活 providerconsumer 端的指標(biāo)監(jiān)控稼跳。
  • TimeoutFilter:如果調(diào)用時(shí)間超過(guò)設(shè)置的 timeout 就打印 Log,但是不要阻止服務(wù)器的運(yùn)行。
  • ExceptionFilter:非檢測(cè)的異常將會(huì)為 ERROR 級(jí)別記錄在 Provider 端吃沪。非檢測(cè)的異常是未在接口上聲明的未經(jīng)檢查的異常.dubbo 會(huì)將在這在 API 包中未引入的異常包裝到RuntimeException中汤善。

以上就是 dubbo 框架在 provider 端的默認(rèn) Filter 擴(kuò)展,當(dāng)然如果你有需求也可以自定義 Filter 擴(kuò)展巷波。具體可以參考 dubbo 官網(wǎng)的 調(diào)用攔截?cái)U(kuò)展萎津。

3卸伞、調(diào)用服務(wù)并響應(yīng) consumer

provider 端通過(guò)接收 consumer 的請(qǐng)求并且解碼抹镊,然后調(diào)用 provider 的一系列自定義擴(kuò)展。下面就是調(diào)用服務(wù)端暴露服務(wù)的真正實(shí)現(xiàn)了荤傲。在進(jìn)行服務(wù)暴露的時(shí)候最終會(huì)調(diào)用 SPI 接口 ProxyFactory (默認(rèn)是
JavassistProxyFactory) 來(lái)獲取 Invoke垮耳。我們可以來(lái)看一下 dubbo 官網(wǎng)對(duì)于服務(wù)提供者暴露一個(gè)服務(wù)的詳細(xì)過(guò)程:

dubbo_rpc_export.jpg

下面我們來(lái)看一下 JavassistProxyFactory 的源代碼:

JavassistProxyFactory .java

public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

在這里需要說(shuō)明 ProxyFactory#getInvoker 這個(gè)方法的三個(gè)請(qǐng)求參數(shù):

  • proxy : 暴露接口服務(wù)的具體實(shí)現(xiàn)類,比如 dubbo-demo-provider 中的 org.apache.dubbo.demo.provider.DemoServiceImpl 實(shí)例對(duì)象遂黍。
  • type : 暴露接口服務(wù)的 Class 對(duì)象终佛,比如 dubbo-demo-api 中的 org.apache.dubbo.demo.DemoService 的 Class 實(shí)例對(duì)象。
  • url : 暴露接口服務(wù)的配置信息雾家。具體信息如下:
registry://localhost:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.75.1%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind.ip%3D192.168.75.1%26bind.port%3D20880%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D3900%26qos.port%3D22222%26side%3Dprovider%26timestamp%3D1530184958055&pid=3900&qos.port=22222&registry=zookeeper&timestamp=1530184958041

然后進(jìn)行服務(wù)調(diào)用的時(shí)候最終就會(huì)調(diào)用到暴露接口服務(wù)的具體實(shí)現(xiàn)類铃彰,也就是 DemoServiceImpl。最終返回的結(jié)果如下:

Result.png

HeaderExchangeHandler 再通過(guò) DubboInvoke 調(diào)用到了暴露接口服務(wù)的真正實(shí)現(xiàn)芯咧,并獲取到返回值時(shí)牙捉。它還需要通過(guò) HeaderExchangeHandler 也就是它自身把響應(yīng)發(fā)送給 consumer竹揍。具體的調(diào)用時(shí)序圖如下:

Provider.jpg

可以看到 dubbo 在響應(yīng) consumer 時(shí)最終也是通過(guò) netty 來(lái)進(jìn)行網(wǎng)絡(luò)通信的。

4邪铲、服務(wù)調(diào)用總結(jié)

當(dāng)服務(wù)越來(lái)越多芬位,容量的評(píng)估,小服務(wù)資源的浪費(fèi)等問(wèn)題逐漸顯現(xiàn)带到,此時(shí)需增加一個(gè)調(diào)度中心基于訪問(wèn)壓力實(shí)時(shí)管理集群容量昧碉,提高集群利用率。此時(shí)揽惹,用于提高機(jī)器利用率的資源調(diào)度和治理中心(SOA)是關(guān)鍵被饿。

  • 服務(wù)容器負(fù)責(zé)啟動(dòng),加載永丝,運(yùn)行服務(wù)提供者锹漱。
  • 服務(wù)提供者在啟動(dòng)時(shí),向注冊(cè)中心注冊(cè)自己提供的服務(wù)慕嚷。
  • 服務(wù)消費(fèi)者在啟動(dòng)時(shí)哥牍,向注冊(cè)中心訂閱自己所需的服務(wù)。
  • 注冊(cè)中心返回服務(wù)提供者地址列表給消費(fèi)者喝检,如果有變更嗅辣,注冊(cè)中心將基于長(zhǎng)連接推送變更數(shù)據(jù)給消費(fèi)者。
  • 服務(wù)消費(fèi)者挠说,從提供者地址列表中澡谭,基于軟負(fù)載均衡算法,選一臺(tái)提供者進(jìn)行調(diào)用损俭,如果調(diào)用失敗蛙奖,再選另一臺(tái)調(diào)用。
  • 服務(wù)消費(fèi)者和提供者杆兵,在內(nèi)存中累計(jì)調(diào)用次數(shù)和調(diào)用時(shí)間雁仲,定時(shí)每分鐘發(fā)送一次統(tǒng)計(jì)數(shù)據(jù)到監(jiān)控中心。

當(dāng)分析了整個(gè) dubbo 從服務(wù)暴露到服務(wù)引用琐脏,然后再分析了 dubbo 的集群調(diào)用 以及 consumer 與 provider 的調(diào)用細(xì)節(jié)之后攒砖。再來(lái)看 dubbo 的調(diào)用圖是不是另外有一番滋味。

參考資料:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末日裙,一起剝皮案震驚了整個(gè)濱河市吹艇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昂拂,老刑警劉巖受神,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異格侯,居然都是意外死亡鼻听,警方通過(guò)查閱死者的電腦和手機(jī)樟结,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)精算,“玉大人瓢宦,你說(shuō)我怎么就攤上這事』矣穑” “怎么了驮履?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)廉嚼。 經(jīng)常有香客問(wèn)我玫镐,道長(zhǎng),這世上最難降的妖魔是什么怠噪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任恐似,我火速辦了婚禮,結(jié)果婚禮上傍念,老公的妹妹穿的比我還像新娘矫夷。我一直安慰自己,他們只是感情好憋槐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布双藕。 她就那樣靜靜地躺著,像睡著了一般阳仔。 火紅的嫁衣襯著肌膚如雪忧陪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天近范,我揣著相機(jī)與錄音嘶摊,去河邊找鬼。 笑死评矩,一個(gè)胖子當(dāng)著我的面吹牛叶堆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播稚照,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蹂空,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼俯萌!你這毒婦竟也來(lái)了果录?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤咐熙,失蹤者是張志新(化名)和其女友劉穎弱恒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體棋恼,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡返弹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年锈玉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片义起。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拉背,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出默终,到底是詐尸還是另有隱情椅棺,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布齐蔽,位于F島的核電站两疚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏含滴。R本人自食惡果不足惜诱渤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谈况。 院中可真熱鬧勺美,春花似錦、人聲如沸碑韵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泼诱。三九已至坛掠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間治筒,已是汗流浹背屉栓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耸袜,地道東北人友多。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像堤框,于是被迫代替她去往敵國(guó)和親域滥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容