Dubbo源码深度解析(四)

         接上篇博客《Dubbo源码深度解析(三)》,上篇博文,主要讲的是DubboBootstrap#start()方法中调用到的其他方法,以及讲到ServiceConfig#export()方法的调用链路。其中讲到最核心的方法为ServiceConfig#doExportUrlsFor1Protocol(),还没讲完,今天接着讲,上篇讲到了这里:

361441beebec41b8a9e36f381424c991.png

        看看PROXY_FACTORY#getInvoker()方法,发现ProxyFactory也是由ExtensionLoader加载的,代码如下:

f8620f0056754ac7977c137329ba5cdb.png

e7b8014443084d9e8866d9176d25d607.png

9f672c3cbdeb4bb7ba3c6434efeadfa3.png

bca33932c1934cc591952d4019fd3ea5.png

       看看ProxyFactory接口,代码如下:

f5a914fb79134eb583435476f9df660d.png

        看看ProxyFactory的实现类,代码如下:

0c9272be5d42418497b0d5377fca4e1e.png

eb58394a04ab48b1b33f67737dc6adb6.png

f36f4e3aaae4431e988f7367eda469b6.png

        发现这三个实现类均没有被@Adaptive注解修饰,因此最终会调用到ExtensionLoader#createAdaptiveExtensionClass(),动态生成ProxyFactory接口实现的字节码对象,看看该方法:

ab6fff1b3d7c421e950adf178294b11c.png

        看看AdaptiveClassCodeGenerator#generate()方法,代码如下:

472b98a55e1547cdb71593012e91e150.png

        看看AdaptiveClassCodeGenerator#generateMethod()方法,代码如下:

06eb7d26d5804d84b67abc44f7876ccc.png

a392958827394047a5f2f0d36772725c.png        看看最后生成的ProxyFactory的实现类,实现类如下:

a9c4e2b2924a4b63b73ade08f2339289.png

        因此,调用PROXY_FACTORY#getInvoker()方法,实际上是调用ProxyFactory$Adaptive#getInvoker()方法,如果传入的url中,proxy属性(可以在@DubboService注解上指定)不为空,则获取属性值,如果为空,则用默认的值"javassist",最终调用ExtensionLoader#getExtensionLoader(ProxyFactory.class).getExtension("javassist")方法,获取具体的实现类,而名字叫"javassist"的ProxyFactory接口的实现类,是JavassistProxyFactory,最终调用的是JavassistProxyFactory#getInvoker()方法,代码如下:

14b0dcc637374dd9b900272b7b9e1a7b.png

        由上图可知,Wrapper#getWrapper()方法也很重要,看看该方法,代码如下:

98b933da5b744df78863430c3a08f253.png

a4b3dd02cc2d4342948e4f7d647f5551.png

        调用Wrapper#getWrapper()方法,传入的接口是HelloService.class,最终生成的返回的Wrapper对象实际上是继承了Wrapper(它是抽象类),Wrapper0,大概长这个样子,代码如下:

87a21629db7e4cfba2d7411f3ed430c0.png

        看看HelloService接口,代码如下:

95db7a72d518495bb609c94067707e50.png

        到这里很清晰了:调用Wrapper#getWrapper()方法,最终生成的是Wrapper类的实现类的对象,实现类的命名为:

8d8b0d6780c342a5b036be0150b6e8a7.png

        生成的实现类,最终会缓存到Wrapper的WRAPPER_MAP属性中,代码如下:

f2e4b31f5b0a4a169348060e3d032f2d.png

        最终调用JavassistProxyFactory#getInvoker()方法,返回的是AbstractProxyInvoker的实现类,代码如下:

39d222ae921645b7837c429b29756135.png

        再回到ServiceConfig#doExportUrlsFor1Protocol()方法中,调用PROXY_FACTORY.getInvoker()方法的地方,代码如下:

cc8dfe0a5bb246269a2b60dbf4676bab.png

        可以知道,返回的invoker对象是AbstractProxyInvoker的子类,又对invoker做了一层包装,即DelegateProviderMetaDataInvoker对象,调用其有参构造,传入invoker对象。再调用PROTOCOL#export()方法,传入DelegateProviderMetaDataInvoker对象,而PROTOCOL属性又和PROXY_FACTORY属性类似,如下:

0e80375485c744009ce3b0dff62f94c6.png

        看看Protocol接口,代码如下:

1d408a95566e4394aab9b78f0bf49a27.png

       发现Protocol接口的子类也都没有被@Adaptive注解修饰,因此也会动态生成Protocol接口的实现类,代码如下(截取部分):

c66ea7815fd64133a90bba75a53e0d88.png

        最终也是寻找一个name叫"registry"的Protocol接口的实现类,看看org.apache.dubbo.rpc.Protocol文件的内容,可知实现类是InterfaceCompatibleRegistryProtocol

7cd2a76e89fc483cadf62d84903dfc77.png这个说法对,但不全对,最终肯定InterfaceCompatibleRegistryProtocol,还记得我在《Dubbo源码深度解析(二)》中讲Dubbo的SPI机制吗?里面提到了包装类型,这里就用到了,先看看Protocol接口的的实现类,如下:

4bfc32687d904c81ae4745b7edd5d23e.png

        看看其中一个包装类,如ProtocolFilterWrapper,代码如下:

1097f144bc0847ba958259426954d4f6.png

        再回过头看看Protocol$Adaptive#export()方法,最终调用的是 Protocol extension = (Protocol) ExtensionLoader#getExtensionLoader(Protocol.class).getExtension("registry")方法,代码如下:

        上图中的instance对象肯定是InterfaceCompatibleRegistryProtocol对象,但是还调用了ExtensionLoader#injectExtension()方法,并传入了instance对象,实际上是给instance对象进行属性注入,看看该方法,代码如下:

        再看看排序,即WrapperComparator.COMPARATOR类,代码如下:

6bc97d1fed6441cf85af148eb1036bed.png

        可只是按照正序排列,也就是order越小,在越靠前,解析到了两个包装类,分别是ProtocolFilterWrapper和ProtocolListenerWrapper,代码如下:

89dd60ea1fe94b11a1e5672903150d15.png

9d2a95519b3040ccbe27d0e2c7f3f4e7.png

        因此,缓存的包装类的顺序也是ProtocolFilterWrapper、ProtocolListenerWrapper,但是又调用了Collections.reverse()方法,因此最终的顺序是:ProtocolListenerWrapper、ProtocolFilterWrapper(先顺序排列再反转,相当于直接是倒序排),再进行循环,代码如下:

3c28496891b8406c939cea696dafadae.png

因此ProtocolFilterWrapper对像的protocol属性是ProtocolListenerWrapper对象,ProtocolListenerWrapper对象的protocol属性是InterfaceCompatibleRegistryProtocol对象,而InterfaceCompatibleRegistryProtocol对象的protocol属性是Protocol$Adaptive,打断点验证如下:

121e88230cfb4063825c919d5c4bcb9f.png

71350bdbddc9469387ba6995ec29f6a7.png

        因此,最终调用的是ProtocolFilterWrapper#export()方法,代码如下:

098267ea7f914c58ac50cd7300d12db1.png

eb95db613dd0475380c7ab7a93de9fc6.png

        看看RegistryProtocol#doLocalExport()方法,代码如下:

        而这里传入的参数protocol属性在前面说过,实际上是Protocol$Adaptive对象,相当于调用的又是Protocol$Adaptive#export()方法,但是此时传入的url为 providerUrl,跟之前的不太一样,由上面打断点可知,providerUrl的protocol属性为"dubbo",因此最终得到的结果对象为:ProtocolFilterWrapper对像,而ProtocolFilterWrapper对像的protocol属性是ProtocolListenerWrapper对象,ProtocolListenerWrapper对象的protocol属性是DubboProtocol,打断点验证,结果如下:

        前面两个对象的export()方法就不看了,直接看DubboProtocol#export()方法,代码如下:

        可知最终创建服务器是通过Exchangers#bind()方法,传入url和requestHandler,而requestHandler则是DubboProtocol的内部类对象,截取部分代码如下:

        看看Exchangers#bind()方法,代码如下:

        这里又涉及到SPI,默认的实现类名为"header",这里就不在多过解释,实现类就是HeaderExchanger。最终,调用的是HeaderExchanger#bind()方法,代码如下:

        看看Transporters#bind()方法,代码如下:

        又涉及到SPI机制,看看Transporter接口,代码如下:

        因此,直接看NettyTransporter#bind()方法,代码如下:

        上图中传入的handler属性是DecodeHandler对象,DecodeHandler对象的handler属性为HeaderExchangeHandler对象,而HeaderExchangeHandler对象的handler属性为DubboProtocol#ExchangeHandlerAdapter对象,断点验证,结果如下:

        在有参构造中,又对handler做了包装,如下:

        这里,又涉及到了SPI,也就是找Dispatcher接口的实现类,看看这接口,代码如下:

        因此可以找到一个实现类,即AllDispatcher,因此,最终的handler为:MultiMessageHandler对象,MultiMessageHandler对象的andler属性为HeartbeatHandler对象,而HeartbeatHandler对象的handler属性为AllDispatcher对象,而AllDispatcher对象的handler属性为DecodeHandler对象,而DecodeHandler对象的handler属性为HeaderExchangeHandler对象,而HeaderExchangeHandler对象的handler为DubboProtocol.ExchangeHandlerAdapter对象(内部类)。断点验证一下,结果如下:

        (不得不吐槽一下,Dubbo层层包装、大量代理的使用、动态生成字节码、以及基于URL的驱动,各种改URL的属性,比如protocol属性(即协议)等,是真的复杂!这些细节,全网有几个人讲得比我清楚、仔细呢?我这么说,应该没人反对吧😊😊😊)

        言归正传,继续看父类的有参构造方法,涉及到赋初始值或者默认值,代码如下:

        再看看AbstractServer#doOpen()方法,代码如下:

        重点看看NettyServer#doOpen()方法,代码如下:

        其中,bootstrap.bind()方法是绑定ip/port,channelFuture.syncUninterruptibly()方法一个同步方法,阻塞主线程,避免程序跑完直接就结束了。

        这块就是Netty Server启动的核心,也是固定写法,任何一个框架,使用Netty作为其通讯框架,大概都是这样写,没什么好讲的,如果对 Netty框架感兴趣,不妨看我写的这篇博客《Netty源码深度解析》,里面对Netty原理有详细的介绍。我假设你对Netty比较熟,看到上面这块代码,我相信你会毫不犹豫的看这个类:NettyServerHandler,它是处理客户端的连接、请求、响应、断开连接的核心类。因此直接看这个类即可。

        ① 如果此时有一个客户端发起了连接,连接成功,调用NettyServerHandler#channelActive()方法;

        ② 客户端发来请求,调用NettyServerHandler#channelRead()方法;

        ③ 客户端断开连接,调用NettyServerHandler#channelInactive()方法;

        ④ 向客户端响应,调用NettyServerHandler#write()方法;

        重点②④,先看看NettyServerHandler#channelRead()方法,代码如下:

        到这里,需要看看DecodeHandler#decode()方法,但是在将这个方法之前,需要先看看编解码。之前不是提到过编码、解码吗?如果是客户端发过来的请求,在客户端放已经对请求做过编码,因此服务端接收情请求,需要进行解码。而且客户端发过来的数据,肯定也是二进制的,因此需要处理,因为到NettyServerHandler这边,接收到的,就变成了 Object message,而不是byte[] message,更加可以确定解码类是将接收到的二进制数据转成了 Object message,并且可能还会涉及到粘包拆包等,OK,看看Dubbo是怎么做的,直接看InternalDecoder#decode()方法,代码如下:

        其中,  codec属性实际上是在这里赋值的,代码如下:

        可以知道,最终调用到DubboCodec#decode()方法,实际上DubboCodec没有decode()方法,而是在其父类中实现的,即调用ExchangeCodec#decode()方法,代码如下:

          这里可以打断点看看,结果如下:

        这里的Request的 twoWay属性是在客户端设置的,并放在了请求头中,如果远程调用需要服务端响应结果则为true;不需要响应则为 false。而Request的 mData属性为DecodeableRpcInvocation对象。再回到HeaderExchangeHandler#received()方法,代码如下:

        到这里为止,Dubbo的解码就讲完了,再回到DecodeHandler#decode()方法,代码如下:

        再看看DecodeableRpcInvocation#decode()方法,代码如下:

        打断点看看,结果如下:

        再回到DecodeableRpcInvocation#received()方法,继续往下看,代码如下:

        重点看看DubboProtocol.ExchangeHandlerAdapter#reply()方法,代码如下:

        看看DubboProtocol#getInvoker()方法,是如何获取Invoker对象的,代码如下:

        那DubboExporter对象是什么放入DubboProtocol的exporterMap属性中的呢?实际上是在这里,代码如下:

        是不是讲到现在,很多断断续续的东西就慢慢接上了呢?这就是读源码的乐趣,希望我写的博客,能给各位看官带来一点点思考和收获😊。下一篇博客地址为:《Dubbo源码深度解析(五)​​​​​​​》

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值