朝花夕拾---dubbo源码分析

前言

该篇博客意在做dubbo启动调用流程做源码分析,采用先给出部分结论,然后推导出整个调用流程的过程

一  服务发布

dubbo服务的每个标记了@service的类和在xml配置中带有标签<dubbo:service>,均会被解析成ServiceBean,服务发布的故事就从serviceBean这个类开始

首先看一下serviceBean的类继承关系:

从类继承图中,我们发现了几个重要的接口:

serviceConfig       这个是和dubbo服务发布相关的

ApplicationListener    spring的事件相关

InitializingBean       初始化相关

当然最重要的就是ApplicationListener的监听方法了:

 以下是服务发布的几个重要路径:

ServiceBean#export();

     ServiceConfig#doExport()

              doExportUrlsFor1Protocol()

在doExportUrlsFor1Protocol这里就是服务发布的重点了

 以上截图就是dubbo在做服务发布的核心代码了,这里dubbo使用了动态代理(SPI)+装饰器模式,完整的构造了一个Invoker和Exporter

DUBBO的SPI:ExtensionLoader  通过加载指定路径下,配置的key-value形式的类。同时还支持代理和增强(Wrapper + 装饰),有兴趣的同学可以认真研究一下dubbo的SPI机制,这里只做简单概括,比如里面2个最重要的代理对象:proxyFacotory,protocol,他们的源码分别截图如下:

proxyFactory:

protocol:

 从以上截图不难发现:protocol和proxyFactory的处理逻辑大体类似:通过url获取指定参数,如果为空,就使用默认值:然后通过ExtensionLoader加载从url中获取的对象

首先看一下通过proxyFactory获取的Invoker对象:

 想一下,为什么extName是javassist,获取到的ProxyFactory却是:StubProxyFactoryWrapper?按照之前说的SPI机制,应该获取的是:JavassistProxyFactory?

这是因为:在ExtensionLoader#createExtension时

 

 cachedWrapperClasses的写入是在读取指定路径的文件时写入的,通过判断加载的类是否有,包含相同接口的构造器做判断的:

 

这里真正干活的还是:JavassistProxyFactory

所以,真正构建Invoker的代码如下:

其实就是构建了一个AbstractProxyInvoker,其中的Wrapper简单的理解成一个包装类即可(这就是dubbo动态生成类的方式,其实没有什么多大的技术问题,只是觉得就是装X而已),代码如下:

 

再来看看export过程:

 同样的,extName是registry,实际的加载类是:ProtocolListenerWrapper

 

 而ProtocolListenerWrapper里面包装的protocol是:ProtocolFilterWrapper,而ProtocolFilterWrapper里面包装的protocol是RegistryProtocol(真正干活的)

可以看下最终生成的Exporter的结构:

 

其中DubboExporter是在DubboProtocol类中构造的,还是和之前一样,通过装饰器,一层一层递进构造的:

至此,服务的发布已经完成,其中有些步骤跳跃可能稍微大一点,感兴趣的同学请自行本地调试 

二  服务调用

dubbo中每一个标记了@Reference域和在xml中配置了<dubbo:reference>的元素,均会被解析成一个ReferenceBean

首先,看一下ReferenceBean的类关系:

其中FactoryBean就是spring的工厂Bean,和ServiceBean类似,ReferenceBean也继承一个Config类,如上,服务调用的故事就从ReferenceConfig的#get()开始

ReferenceBean#getObject()

    ReferenceConfig#get()

         init();

相信使用过动态代理的同学都知道,使用动态代理的使用,通过代理对象,在自己的逻辑里面就可以"为所欲为"了。dubbo在服务调用的时候也是这么干的!

init方法就是创建代理的入口,方法比较长,咱们捡重点说,前面就是一些校验,判断,最主要的方法入口:

 创建代理需要一个map,看看这个map里面的信息如下:

 下面正式开启创建代理的过程,老规矩,忽略掉无关精要的代码,直接上干货:

 这个refprotocol就是在前面截图的那个Protocol$Adaptive代码,这里咱们为了方便调试,直接把代码替换一下:

 

 和服务发布过程一样,extName是registry,获取到的实例却是:ProtocolListenerWrapper(装饰器模式)

 大概装饰层级就是:

ProtocolListenerWrapper

   ProtocolFilterWrapper

      RegistryProtocol

 在RegistryProtocol是真正干活的地方:

 一路debug调试,最终返回到创建代理的代码入口:

在之前的服务发布过程中,我们已经帖出了proxyFactory的相关代码(dubbo这里做了动态加载,不利于调试,咱们做个替换)

替换后的代码如下:

现在将要进行真正的动态代理的创建过程:

 和其他的Adaptive一样,真正干活的是JavassistProxyFactory,使用StubProxyFactoryWrapper做装饰

真正干活的是JavassistProxyFactory

 

看到这里,熟悉动态代理的同学已经很熟悉了。真正的执行逻辑就在InvokerInvocationHandler里面了,在这个handler里面是不是为所欲为呢?

截图如下:

由截图可知,在InvokerInvocationHandler里面真正调用就是使用invoker调用逻辑

当真正调用时,一路dubug

 这个Invoker也是一个装饰器。咱们一步一步调试进入:

首先:MockClusterInvoker

 真正的调用又委托给了:FailoverClusterInvoker

 从以上代码注释中可以看出,FailoverClusterInvoker,通过负载均衡器选择"最优"的Invoker发起远程调用

这里选择了:RegistryDirectory$DeleteInvoker其实就是一个InvokerWrapper

 真正干活的是ListenerInvokerWrapper

 继续递进:调用ProtocolFilterWrapper

 最终来到了ProtocolFilterWrapper的匿名内部类:

把调用过程委托给了filter调用链路,至此,Invoker的使命完成了。相信更深的调用逻辑,通过分析Filter就能找到答案了

三  FailOverClusterInvoker选择Invoker过程既FilterChain调用链的组装

在第二节中,有分析服务发布调用过程,最红是委托给了Filter调用。我们在分析的时候,有意的忽略掉了部分调用细节,只是为了更好的梳理出整个调用链路。下面就分析一下在负载均衡器选择Invoker的详细逻辑

故事的开始当然是从FailoverClusterInvoker的invoke开始,FailoverClusterInvoker没有自己实现invoke,其父类AbstractClusterInvoker实现了:

寻找Invoker的时候委托给了RegistryDirectory:

 最终的Invoker寻找在RegistryDirectory里面维护的一个字段:methodInvokerMap里面查询即可

 查询到全量的数据之后,就通过负载均衡器选择,完成Invoker的选择

那RegistryDirectory的methodInvokerMap又是在什么时候获得调用方法和Invoker的对应关系的呢?

通过跟踪字段的调用来源

最终确认: RegistryProtocol#doRefer时会调用到如上方法里面,生成method和Invokers的对应关系

以上,将服务发布,服务调用的过程做了较为粗略和完整的说明

四 结论证明

在前言部分有说过,先给出部分结论:

1 所有被标记了@service和使用<dubbo:service>标记的元素,均会被spring解析成ServiceBean

2 所有被标记了@Reference和使用<dubbo:reference>的元素,均会被spring解析成ReferenceBean

这是怎么做到的呢?

在结合xml使用的时候,dubbo有引入DubboNamespaceHandler,该handler会解析相关的xml标签,完成如上功能:

而在springboot应用中,dubbo有引入:ServiceAnnotationBeanPostProcessor

该类是一个前置处理器,会扫描@service标记的类,通过构建BeanDefinitionBuilder完成ServiceBean的注册:

 同样的:springboot应用中引入了ReferenceAnnotationBeanPostProcessor

ReferenceAnnotationBeanPostProcessor是一个后置处理器,在Bean初始化时会被调用,注入相应的代理类

创作不易,如果对你有帮助,请点赞+收藏!

猜你喜欢

转载自blog.csdn.net/qq_39203337/article/details/126598241