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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容