上一篇博客主要讲服务提供方服务的发布,以及Netty是如何启动的,客户端发过来的请求,会经过哪些处理器,以及补充之前没讲完的SPI机制等等。这篇博客将会接着继续讲,在看这篇博客之前,请先看上一篇博客:《Dubbo源码深度解析(四)》。
上一篇讲到了DubboProtocol的exporterMap属性的设置,代码如下:
存入的值究竟是什么对象?我觉得有必要彻底搞清楚。还是回到ServiceConfig#doExportUrlsFor1Protocol()方法,直接看关键的地方,代码如下:
因此到这里为止,可以知道,调用Protocol$Adaptive#export()方法,传入的Invoker对象为RegistryProtocol.InvokerDelegate对象,而RegistryProtocol.InvokerDelegate对象invoker是DelegateProviderMetaDataInvoker对象,而DelegateProviderMetaDataInvoker对象的invoker属性为抽象类AbstractProxyInvoker的匿名实现类。最终调用的是AbstractProxyInvoker匿名实现类的 doInvoke()方法,而该方法调用的是 Wrapper实现类的invokeMethod()方法,打断点验证:
到这里还没完,再看看Protocol$Adaptive#export()方法,代码如下:
因此,看ProtocolFilterWrapper#export()方法,代码如下:
重点看看ProtocolFilterWrapper#buildInvokerChain()方法,代码如下:
这里也涉及到SPI,不详细讲了,直接看org.apache.dubbo.rpc.Filter文件,内容如下:
就是获取这些实现类,只不过指定了group为"provider",举例,如下面这两种:
最终的结果为多个FilterNode对象嵌套,其中FilterNode对象的filter属性是Filter对象,next属性还是FilterNode对象,只不过它的filter属性变了。打断点,结果如下:
再调用ProtocolListenerWrapper#export()方法,代码如下:
再看看RegistryProtocol#doLocalExport方法,代码如下:
回到ServiceConfig#doExportUrlsFor1Protocol()方法核心的地方法,代码如下:
可知返回的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抽象类的匿名实现类。断点验证,结果如下:
由断点结果来看,符合预期(层层嵌套....在看代码的时候,不做笔记,很容易漏掉)。当然,这里的RegistryProtocol.DestroyableExporter对象也不是那么重要,重要的是DubboExporter对象,因为最终获取的是DubboExporter#getInvoker()方法,获取Invoker对象,并执行Invoker#invoke()方法,直接看调用Invoker#invoke()方法的地方,代码如下:
看看FilterNode#invoke()方法,代码如下:
(注:每个Filter的实现类都提供了不同的功能,这里我就不讲了,有兴趣的自己看) 剩下要经过的的Filter实现类为以下这些,Filter#invoker()方法分别是为:
最后一个Filter为ExceptionFilter,在ExceptionFilter#invoke()方法中,才会真正的调用Invoker#invoke()方法,代码如下:
看看AbstractProxyInvoker#invoke()方法,代码如下:
在JavassistProxyFactory中创建了AbstractProxyInvoker抽象类的匿名实现类,并且实现了AbstractProxyInvoker#doInvoke()方法,代码如下:
可知,最终是调用Wrapper实现类的invokeMethod()方法,只不过这个实现类是加载String动态动态生成的字节码对象,反射调用构造方法传创建的,这在前面提到过,这里不再赘述。但是我把Wrapper实现类.java文件弄出来了,它的invokeMethod()方法为:
最后得到结果,返回。顺便看看返回执行的逻辑是怎样的,还是回到NettyServerHandler#channelRead()方法,代码如下:
看看NettyChannel#getOrAddChannel()方法,代码如下:
看看HeaderExchangeChannel#getOrAddChannel()方法,看看返回的ExchangeChannel的实现类是哪个,代码如下:
断点看看,结果如下:
上图调用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对象,如添加编码类,代码如下:
之前聊到解码的时候没详细讲调用链路,这里我在讲编码的时候,讲细一点。这里的编码是通过InternalEncoder完成的,看看该类,代码如下:
看看InternalEncoder的继承图,结果如下:
可知InternalEncoder实现了ChannelOutboundHandler,最终消息发出去之前,肯定会调用ChannelOutboundHandler#write()方法,具体的实现是在InternalEncoder的父类MessageToByteEncoder#write()方法,代码如下:
再看看InternalEncoder#encode()方法,代码如下:
到这里为止,我觉得应该是把服务提供方目标方法的调用,以及执行结果是如何响应给客户端,讲解得非常清楚了。同时,还顺便提了一点Netty相关的代码,希望我讲的,能给各位带来一点启发。
再回到RegistryProtocol#export()方法,还会干一件事,也就是将服务提供方的ip/port等信息注册到注册中心,核心代码如下:
先看看RegistryProtocol#getRegistry()方法,代码如下:
由于RegistryProtocol是通过Dubbo的SPI机制加载创建的,之前也提到过,通过Dubbo的SPI创建对象的时候,会找出所有没有被@DisableInject修饰的Setter方法,然后从Spring容器中或者Dubbo的SPI找对应的类的对象,通过反射调用Setter方法进行赋值,很显然RegistryFactory对象在Spring容器中是没有,因此是通过进行Dubbo的SPI的,看看RegistryFactory接口,代码如下:
由于依赖了Nacos相关的依赖,因此还需要在dubbo-registry-nacos模块下找org.apache.dubbo.registry.RegistryFactory文件,结果如下:
因此可以知道,最终的结果RegistryProtocol的registryFactory属性为RegistryFactoryWrapper对象,而RegistryFactoryWrapper对象的registryFactory属性为NacosRegistryFactory对象,断点验证,结果如下:
因此,直接看RegistryFactoryWrapper#getRegistry()方法即可,代码如下:
再看AbstractRegistryFactory#getRegistry()方法,代码如下:
顺便看看NacosRegistry的类继承结构,结果如下:
由此可知,注册服务的核心当然是调用ListenerRegistryWrapper#register()方法,代码如下:
最终在Nacos上展示的元数据信息也是上面这些,看看Nacos控制台,结果如下:
这就是Dubbo往Nacos注册实例的大概逻辑,至于Nacos是如何处理注册请求,后续我会专门写一篇博客讲解。以上,就是本篇博客的全部内容。下一篇,我将会讲服务消费方相关的逻辑,讲解就不会很仔细,因为很多东西在本篇博客以及之前的博客,讲得很清楚了。博客地址:《Dubbo源码深度解析(六)》