前言
该篇博客意在做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初始化时会被调用,注入相应的代理类
创作不易,如果对你有帮助,请点赞+收藏!