Dubbo源码深度解析(五)

        上一篇博客主要讲服务提供方服务的发布,以及Netty是如何启动的,客户端发过来的请求,会经过哪些处理器,以及补充之前没讲完的SPI机制等等。这篇博客将会接着继续讲,在看这篇博客之前,请先看上一篇博客:《Dubbo源码深度解析(四)》。

        上一篇讲到了DubboProtocol的exporterMap属性的设置,代码如下:

a757f835289d44dfad07060e1bc02950.png

        存入的值究竟是什么对象?我觉得有必要彻底搞清楚。还是回到ServiceConfig#doExportUrlsFor1Protocol()方法,直接看关键的地方,代码如下:

246b5b84ac674451aa3d87330198bdad.png

98e5a7526b0440d5b22965e7f1e91bae.png

14613d040f154b50acbd53670be85221.png

        因此到这里为止,可以知道,调用Protocol$Adaptive#export()方法,传入的Invoker对象为RegistryProtocol.InvokerDelegate对象,而RegistryProtocol.InvokerDelegate对象invoker是DelegateProviderMetaDataInvoker对象,而DelegateProviderMetaDataInvoker对象的invoker属性为抽象类AbstractProxyInvoker的匿名实现类。最终调用的是AbstractProxyInvoker匿名实现类的 doInvoke()方法,而该方法调用的是 Wrapper实现类的invokeMethod()方法,打断点验证:

0aea713fbcf24392b68cae01bca4ce88.png

        到这里还没完,再看看Protocol$Adaptive#export()方法,代码如下:

abcd3b737eff47839535404bf2285539.png

        因此,看ProtocolFilterWrapper#export()方法,代码如下:

9668abdb537448e282b94fedbb5bcd7f.png

        重点看看ProtocolFilterWrapper#buildInvokerChain()方法,代码如下:

41b94452f6f549e4a441b0f15712f03d.png

af2b3734edaf48ecb1f74de84af1a238.png

        这里也涉及到SPI,不详细讲了,直接看org.apache.dubbo.rpc.Filter文件,内容如下:

3983f7d0b87e420281b66caf6b97e750.png

        就是获取这些实现类,只不过指定了group为"provider",举例,如下面这两种:

ec50c2614204475387dd5c132c753909.png

b9e7ba00804248c09574a1d6510c9335.png

        最终的结果为多个FilterNode对象嵌套,其中FilterNode对象的filter属性是Filter对象,next属性还是FilterNode对象,只不过它的filter属性变了。打断点,结果如下:

6bed1614cf9a4e6a96065ab2a358592d.png

        再调用ProtocolListenerWrapper#export()方法,代码如下:

f623b8a1058d4feb9742a5fed5e6111a.png

bb07e588b07647b9a034393408b5ee44.png

d95214a035e843b7bf7c1d3e8650c423.png

c48f57fe6a7541cb8d0c07d01234429b.png

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

ae2580aaf9ed4dddaff942d823099b0b.png

        回到ServiceConfig#doExportUrlsFor1Protocol()方法核心的地方法,代码如下:

345dc0ea550e4390ba073b5f6b462533.png

        可知返回的exporter为RegistryProtocol.DestroyableExporter对象,而RegistryProtocol.DestroyableExporter对象的exporter属性为RegistryProtocol.ExporterChangeableWrapper对象,而RegistryProtocol.ExporterChangeableWrapper对象的exporter属性为ListenerExporterWrapper对象,而ListenerExporterWrapper对象的exporter属性为DubboExporter对象,而DubboExporter对象的invoker属性为FilterNode对象链,而FilterNode的filter是前面读取的Filter对象的实现类(被@Activate注解修饰,切group的值为"provider"),FilterNode的next属性还是FilterNode对象,最后一个FilterNode对象的next属性为RegistryProtocol.InvokerDelegate对象而RegistryProtocol.InvokerDelegate对象的invoker属性为DelegateProviderMetaDataInvoker对象,而DelegateProviderMetaDataInvoker对象那个的invoker属性为AbstractProxyInvoker抽象类的匿名实现类。断点验证,结果如下:

0e30d5a01c2e4be08a9d75ac244cf432.png

        由断点结果来看,符合预期(层层嵌套....在看代码的时候,不做笔记,很容易漏掉)。当然,这里的RegistryProtocol.DestroyableExporter对象也不是那么重要,重要的是DubboExporter对象,因为最终获取的是DubboExporter#getInvoker()方法,获取Invoker对象,并执行Invoker#invoke()方法,直接看调用Invoker#invoke()方法的地方,代码如下:

2d58086cab704e91a214503f7f9f8bd8.png

        看看FilterNode#invoke()方法,代码如下:

e1b7fce3963240949e21d2bb74a6647c.png

4d9500dbc244415b97a9bbfb227a514a.png

        (注:每个Filter的实现类都提供了不同的功能,这里我就不讲了,有兴趣的自己看) 剩下要经过的的Filter实现类为以下这些,Filter#invoker()方法分别是为:

5f8e46a7988146f68c15f34dfa8cd933.png

a6e78c189c8146bb890548c1607ef2a5.png

b3d66ebdefaa4e2e962e279a0097d4aa.png

54d2bd2ea41c4e948b73c45050988962.png

115019d0b2594968a9df7921dca99feb.png

47179b33a3394b1b96a918090f9870ae.png

        最后一个Filter为ExceptionFilter,在ExceptionFilter#invoke()方法中,才会真正的调用Invoker#invoke()方法,代码如下:

fb89764fb51649c78454325acda57306.png

9f27de1febb141df8f716bd314dbbfb0.png

f7fc977d7d68473f9a0194d786069144.png

d332c8d8c91f4b39aa6ff8bbd437d27e.png

        看看AbstractProxyInvoker#invoke()方法,代码如下:

afabe787d4424611abd1ce13a639ac4c.png

        在JavassistProxyFactory中创建了AbstractProxyInvoker抽象类的匿名实现类,并且实现了AbstractProxyInvoker#doInvoke()方法,代码如下:

195e54a1478c44aa8e562045ec71a34d.png

        可知,最终是调用Wrapper实现类的invokeMethod()方法,只不过这个实现类是加载String动态动态生成的字节码对象,反射调用构造方法传创建的,这在前面提到过,这里不再赘述。但是我把Wrapper实现类.java文件弄出来了,它的invokeMethod()方法为:

474944dc629a4c3dbd3dd425f6083f9b.png

        最后得到结果,返回。顺便看看返回执行的逻辑是怎样的,还是回到NettyServerHandler#channelRead()方法,代码如下:

f117b62d185047988c2347bb23147071.png

        看看NettyChannel#getOrAddChannel()方法,代码如下:

51954f50bcdc4aa2a6a0b9f92b58f965.png

f39e916f900d4db08e625e81572aee31.png

6fbbee639c70454bb740393b2cd957e6.png

b67b5dddcb044449a7f38cc682bd1431.png

        看看HeaderExchangeChannel#getOrAddChannel()方法,看看返回的ExchangeChannel的实现类是哪个,代码如下:

cacb0e6f2736473186d1fdfff610e6a8.png

f3c721fb4aa24f4e96080cf7830d4385.png

2a698229c7574d4698a920cc5957dcf6.png

1344c9cd13d54f16bd0082f5d125f8b5.png

        断点看看,结果如下:

5f12464e4a44434aa4d66af71a343922.png

        上图调用Channel#writeAndFlush()方法的时候,并不会马上向客户端发消息,而是经过一系列ChannelHandler链,代码就不追了,对Netty有兴趣的,可以看我写的博客《Netty源码深度解析》。大概是这样:如果是服务端向客户端发消息的话,会经过一系列ChannelOutboundHandler对象。其实就是一个ChannelHandler链,从tail -> head (tail 和  head均为DefaultChannelPipeline类的成员属性,通过AbstractChannelHandlerContext的next和prev属性关联起来,组成双向链表,而ChannelOutboundHandler对象会先包装成DefaultChannelHandlerContext对象,它是AbstractChannelHandlerContext的子类,再添加到tail 和 head之间);如果是客户端给服务端发消息,则是head -> tail (调用的是一系列ChannelInboundHandler#channelRead()方法)。这里就是往DefaultChannelPipeline中添加ChannelOutboundHandler对象,如添加编码类,代码如下:

4844ad22aa2247718e276d2e1dba3d38.png

        之前聊到解码的时候没详细讲调用链路,这里我在讲编码的时候,讲细一点。这里的编码是通过InternalEncoder完成的,看看该类,代码如下:

e914bec1fe9c42a6bf3b87358faaf1f9.png

        看看InternalEncoder的继承图,结果如下:

2ffb77cf00384be98d08c88e10749534.png

        可知InternalEncoder实现了ChannelOutboundHandler,最终消息发出去之前,肯定会调用ChannelOutboundHandler#write()方法,具体的实现是在InternalEncoder的父类MessageToByteEncoder#write()方法,代码如下:

73900cbd93734d808395ab70a8d81ecf.png

8ef21766367f47a4b9d333352be8cedc.png

        再看看InternalEncoder#encode()方法,代码如下:

34fde6b9452b40ef98d4e76d3103b540.png

720f9af7eed74607aea35db671452086.png

3d4313a093484c968adb2112f7486df2.png

744d102079e641ff85145b742d52d3a3.png

c9b2069e9f4946cfae32a432d9359c0c.png

        到这里为止,我觉得应该是把服务提供方目标方法的调用,以及执行结果是如何响应给客户端,讲解得非常清楚了。同时,还顺便提了一点Netty相关的代码,希望我讲的,能给各位带来一点启发。

        再回到RegistryProtocol#export()方法,还会干一件事,也就是将服务提供方的ip/port等信息注册到注册中心,核心代码如下:

d949729ea3bf4c1e89969e0f6c59a7bf.png

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

917858a2656f454ebde01e3be6dd32da.png

5343c5dc15bd4bfa822eabd67ae2e059.png

        由于RegistryProtocol是通过Dubbo的SPI机制加载创建的,之前也提到过,通过Dubbo的SPI创建对象的时候,会找出所有没有被@DisableInject修饰的Setter方法,然后从Spring容器中或者Dubbo的SPI找对应的类的对象,通过反射调用Setter方法进行赋值,很显然RegistryFactory对象在Spring容器中是没有,因此是通过进行Dubbo的SPI的,看看RegistryFactory接口,代码如下:

f2587c73cfb2459dbdf4942cbacdbf54.png

743cfaa7349f4461b22cb13c259afaa9.png

        由于依赖了Nacos相关的依赖,因此还需要在dubbo-registry-nacos模块下找org.apache.dubbo.registry.RegistryFactory文件,结果如下:

c1190cf82f274e50903d5cbb8b7a283d.png

        因此可以知道,最终的结果RegistryProtocol的registryFactory属性为RegistryFactoryWrapper对象,而RegistryFactoryWrapper对象的registryFactory属性为NacosRegistryFactory对象,断点验证,结果如下:

13d93d928bfa446188ef81df5839a21b.png

        因此,直接看RegistryFactoryWrapper#getRegistry()方法即可,代码如下:

57209635710b4f19ba86c8f1321ad994.png

        再看AbstractRegistryFactory#getRegistry()方法,代码如下:

dabb9f21e2e24a52bd028fa0ac918741.png

7bdf0aa684f3485984aaa6dc2fdad21d.png

        顺便看看NacosRegistry的类继承结构,结果如下:

86916a30121243c18c6dde1cb681006b.png

7dcce161301243fca140ee3cac326345.png

        由此可知,注册服务的核心当然是调用ListenerRegistryWrapper#register()方法,代码如下:

c6735337613a4545ad9fd82f98cefbc0.png

32ad9fa58a2540d5a79edaf29edfbb01.png

e4ed085c017c4307959f67aaa5df1556.png

ed109d52912440a6ab0fb53ed441eeee.png

42bbaad4f21042f6867b04f7783668bc.png

e1c0387088ff49a39fb387492f0cf2b4.png

b99b352a64084654bb03e106db78f9b5.png

ec90304fb6374c3880d7c8b9186570df.png

        最终在Nacos上展示的元数据信息也是上面这些,看看Nacos控制台,结果如下:

f92ef81f6df24644bafe1e48dbc696cc.png

        这就是Dubbo往Nacos注册实例的大概逻辑,至于Nacos是如何处理注册请求,后续我会专门写一篇博客讲解。以上,就是本篇博客的全部内容。下一篇,我将会讲服务消费方相关的逻辑,讲解就不会很仔细,因为很多东西在本篇博客以及之前的博客,讲得很清楚了。博客地址:《Dubbo源码深度解析(六)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值