在前面一篇博客中分享了 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 的 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)求的。
以上就是 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ò)展:
可以看到默認(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)激活provider
與consumer
端的指標(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ò)程:
下面我們來(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®istry=zookeeper×tamp=1530184958041
然后進(jìn)行服務(wù)調(diào)用的時(shí)候最終就會(huì)調(diào)用到暴露接口服務(wù)的具體實(shí)現(xiàn)類铃彰,也就是 DemoServiceImpl。最終返回的結(jié)果如下:
HeaderExchangeHandler 再通過(guò) DubboInvoke 調(diào)用到了暴露接口服務(wù)的真正實(shí)現(xiàn)芯咧,并獲取到返回值時(shí)牙捉。它還需要通過(guò) HeaderExchangeHandler 也就是它自身把響應(yīng)發(fā)送給 consumer竹揍。具體的調(diào)用時(shí)序圖如下:
可以看到 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)用圖是不是另外有一番滋味。
參考資料:
- http://dubbo.apache.org/books/dubbo-user-book/demos/echo-service.html
- http://dubbo.apache.org/books/dubbo-user-book/demos/generic-service.html
- http://dubbo.apache.org/books/dubbo-admin-book/install/simple-monitor-center.html
- http://dubbo.apache.org/books/dubbo-dev-book/impls/filter.html
- http://dubbo.apache.org/books/dubbo-dev-book/implementation.html
- http://dubbo.apache.org/books/dubbo-user-book/preface/architecture.html