dubbo源碼分析23 -- provider 接收與發送原理

在前面一篇博客中分享了 dubbo 在網絡通信當中的 consumer 的發送以及接收原理。通過集羣容錯最終選擇一個合適的 Invoke 通過 netty 直聯調用 provider 的服務。衆所周知, netty 是基於 Java Nio 的 Reactor 模型的異步網絡通信框架,所以 dubbo 在 consumer 端把異步變成了同步。

大概總結了 consumer 的發送與接收原理,下面我們來討論一下 dubbo 網絡通信當中 provider 的接收與發送原理。這樣就完成了 dubbo 架構圖裏面的 consumer 調用 provider 的過程.

dubbo-architecture.png

本次是分析 dubbo 的 provider 的接收與發送原理,討論包括以下幾個點:

  • provider 接收 consumer 請求
  • provider 的擴展點調用
  • provider 響應 consumer 調用
  • dubbo 服務調用總結

1、provider 接收 consumer 請求

同 consumer 一樣 provider 默認也是通過 netty 進行網絡通信的。在之前的分析 dubbo 進行服務暴露(NettyServer#doOpen)的時候, 它是通過 Netty 進行服務暴露,添加了一個 dubbo 的自定義 netty 的 ChannelHandler 也就是 NettyServerHandler 來處理網絡通信事件。下面我們來看一下 provider 是如何接收 consumer 發送過來請求的。

NettyServerHandler.jpg

以上就是 provider 接收 consumer 端的調用圖,可以發現其實是和 consumer 端接收 provider 端的類似都是通過自定義 netty 的 ChannelHandler 也就是 NettyServerHandler 來接收網絡請求。並且同樣的通過 dubbo 自定義的 ChannelHandler 來處理請求。下面我們還是來分析一下這些 dubbo 自定義 ChannelHandler 的作用:

  • MultiMessageHandler:支持 MultiMessage 消息處理,也就是多條消息處理。
  • HeartbeatHandler:netty 心條檢測。如果心跳請求,發送心跳然後直接 return,如果是心跳響應直接 return。
  • AllChannelHandler:使用線程池通過 ChannelEventRunnable 工作類來處理網絡事件。
  • DecodeHandler:解碼 message,解析成 dubbo 中的 Request 對象
  • HeaderExchangeHandler:處理解析後的 consumer 端請求的 Request 信息,把請求信息傳遞到 DubboProtocol 並從 DubboExpoter 裏面找到相應具體的 Invoke 進行服務調用(後面具體分析)。

其實可以看到 consumer 與 provider 接收網絡請求都是通過自定義 netty 的 ChannelHandler。然後通過調用自定義 ChannelHandler#channelRead (其實是 ChannelHandler 的子接口 ChannelInboundHandler#channelRead )來接收並處理網絡請求。在之前服務暴露分析的時候我們講過AbstractProtocol#exporterMap 也就是 dubbo 在進行服務暴露的時候通過 AbstractProtocol#serviceKey 爲 key 以 DubboExporter(Invoke 轉化成 Exporter) 爲 value 的服務接口暴露信息。然後把請求信息交給 DubboProtocol 根據 consumer 裏面 Request 裏面的 Invocation 請求信息獲取到 DubboExporter。最後通過DubboExporter#getInvoker 獲取暴露服務具體的服務實現,完成整個調用。

我們可以看到 consumer 與 provider 進行網絡接收信息是類似的,相同點都是通過自定義的 ChannelHandler 來處理網絡請求信息。通過 dubbo 這個自定義的 ChannelHandler 來適配不同的 Java Nio 框架,因爲在 AbstractPeer 類中都持有 dubbo 自定義的這個 ChannelHandler 。 dubbo 默認使用的是 netty 作爲 Nio 框架,通過配置 dubbo 還可以以 Mina 與 Grizzly 作爲 Nio 框架。

這個就用到了 dubbo 的核心 SPI 平等的對待第三方框架。

上面我們討論了相同點,下面我們來看一下 consumer 與 provider 接收網絡請求的不同點:

  • consumer 接收的是 provider 端發送過來的 Response(響應信息),而 provider 是接收 consumer 端發送過來的 Request(請求信息)。
  • consumer 最後在 HeaderExchangeHandler 中調用 handleResponse 方法,而 provider 最在是在HeaderExchangeHandler 中調用 handleRequest 方法。
  • consumer 會默認會同步等待 provider 處理後的響應信息(也可以異步處理),而 provider 在處理完成之後就會同步的把響應請求發送給 consumer.

2、provider 的擴展點調用

與 consumer 引用服務一樣, provider 在暴露服務的時候也會有擴展點。 就像 J2EE 調用 Servlet 的時候也可以通過 java.servlet.Filter 進行調用擴展,dubbo 在進行服務暴露方的時候也會有 dubbo 自己的 Filter 擴展。那麼我們就來看一下在進行 Invoke 調用的時候 dubbo 都有哪些擴展:

DubboProtocol.jpg

可以看到默認情況下,dubbo 在進行服務暴露的時候會加上框架自定義的 7 個 Filter 擴展。下面就來簡單描述一下這 7 個 Filter 的作用:

  • EchoFilter:回聲測試,用於檢測服務是否可用,回聲測試按照正常請求流程執行,能夠測試整個調用是否通暢,可用於監控。
  • ClassLoaderFilter:
  • GenericFilter:實現泛化調用,泛接口實現方式主要用於服務器端沒有API接口及模型類元的情況,參數及返回值中的所有POJO均用Map表示,通常用於框架集成.比如:實現一個通用的遠程服務Mock框架,可通過實現GenericService接口處理所有服務請求。
  • TraceFilter:方法調用時間查探擴展器, 通過 TraceFilter#addTracer 添加需要查探類的方法與查探最大次數。當進行方法調用的時如果該方法的調用次數少於傳遞的最大次數就會把方法調用耗時發送給遠程服務。
  • MonitorFilter:MonitorFilter 其實是在分析之前 dubbo monitor 的時候就進行了詳細的分析。它主要是通過<dubbo:monitor protocol="registry" />來激活 providerconsumer 端的指標監控。
  • TimeoutFilter:如果調用時間超過設置的 timeout 就打印 Log,但是不要阻止服務器的運行。
  • ExceptionFilter:非檢測的異常將會爲 ERROR 級別記錄在 Provider 端。非檢測的異常是未在接口上聲明的未經檢查的異常.dubbo 會將在這在 API 包中未引入的異常包裝到RuntimeException中。

以上就是 dubbo 框架在 provider 端的默認 Filter 擴展,當然如果你有需求也可以自定義 Filter 擴展。具體可以參考 dubbo 官網的 調用攔截擴展

3、調用服務並響應 consumer

provider 端通過接收 consumer 的請求並且解碼,然後調用 provider 的一系列自定義擴展。下面就是調用服務端暴露服務的真正實現了。在進行服務暴露的時候最終會調用 SPI 接口 ProxyFactory (默認是
JavassistProxyFactory) 來獲取 Invoke。我們可以來看一下 dubbo 官網對於服務提供者暴露一個服務的詳細過程:

dubbo_rpc_export.jpg

下面我們來看一下 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);
            }
        };
    }

}

在這裏需要說明 ProxyFactory#getInvoker 這個方法的三個請求參數:

  • proxy : 暴露接口服務的具體實現類,比如 dubbo-demo-provider 中的 org.apache.dubbo.demo.provider.DemoServiceImpl 實例對象。
  • type : 暴露接口服務的 Class 對象,比如 dubbo-demo-api 中的 org.apache.dubbo.demo.DemoService 的 Class 實例對象。
  • url : 暴露接口服務的配置信息。具體信息如下:
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

然後進行服務調用的時候最終就會調用到暴露接口服務的具體實現類,也就是 DemoServiceImpl。最終返回的結果如下:

Result.png

HeaderExchangeHandler 再通過 DubboInvoke 調用到了暴露接口服務的真正實現,並獲取到返回值時。它還需要通過 HeaderExchangeHandler 也就是它自身把響應發送給 consumer。具體的調用時序圖如下:

Provider.jpg

可以看到 dubbo 在響應 consumer 時最終也是通過 netty 來進行網絡通信的。

4、服務調用總結

當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個調度中心基於訪問壓力實時管理集羣容量,提高集羣利用率。此時,用於提高機器利用率的資源調度和治理中心(SOA)是關鍵。

  • 服務容器負責啓動,加載,運行服務提供者。
  • 服務提供者在啓動時,向註冊中心註冊自己提供的服務。
  • 服務消費者在啓動時,向註冊中心訂閱自己所需的服務。
  • 註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連接推送變更數據給消費者。
  • 服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一臺提供者進行調用,如果調用失敗,再選另一臺調用。
  • 服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心。

當分析了整個 dubbo 從服務暴露到服務引用,然後再分析了 dubbo 的集羣調用 以及 consumer 與 provider 的調用細節之後。再來看 dubbo 的調用圖是不是另外有一番滋味。

參考資料:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章