dubbo源码分析-服务暴露流程

dubbo源码分析-服务暴露

    • 环境准备
    • 前置知识
    • 服务暴露流程分析
    • 文末
      • 服务暴露总结
      • 关于交流
      • 服务暴露时序图

chapter II
   众所周知,dubbo是一款开源的(现已进入apache孵化器)具备一定服务治理能力的RPC基础框架,可以理解为SOA框架,我们在使用它的过程中,往往只需要简单的几行配置,就可以无感知的在本地调用远程的服务,甚至于如果不去翻阅配置类(配置文件)我们可能都不知道调用的服务不在本地,那么dubbo是怎么做到的呢?我们要想彻底的理解dubbo为我们应用提供服务的工作方式,其实本质就是要去理解dubbo中服务的整个生命周期,在我看来,dubbo服务的生命周期无非就是服务的发布暴露、服务的引用、服务的调用 ,以及服务的销毁 ,当然服务的调用期间可能会穿插一些服务的治理,因此我觉得我们只要将这几个点的逻辑梳理清楚,然后再整个贯穿起来,这样我们不仅可以理解dubbo的整个工作运行流程及原理,推广开来,再去阅读或者使用类似的RPC框架的话,就会轻松一点,因为大多数RPC框架的设计思想是类似的。接下来,秉着这一理念,我们首先尝试着去分析一下dubbo服务发布的整个流程。

环境准备

  基于dubbo2.6.5版本,可以通过github拉取下dubbo的源码,然后切换到一个稳定版本的分支

前置知识

dubbo服务暴露是依托于spring容器启动的,也可以理解为服务的暴露参与到了spring容器的生命周期当中,所以这就要求我们必须掌握spring相关的一些知识。上篇博客已经提到了服务暴露过程中所依赖的spring相关接口。这里我大概说一下这些接口的作用,读者如果不了解的话,可以课下自己找资料学习一下。

服务暴露流程分析

  • 服务暴露启动入口
    从dubbo的demo入手,找到启动类(com.alibaba.dubbo.container.Main),我们看到在启动类中有如下代码:
   private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);

这段代码将在分析spi的机制时具体展开, 我们现在只需要知道这段代码的作用是在运行时获取所有需要启动的容器并且遍历启动,然后才有可能将服务暴露出去。

  • 服务暴露流程
    我们知道服务暴露的核心组件是ServiceBean,我们也知道ServiceBean是在基于spring可扩展的XMLschema实例化来的,也就是说在spring容器启动的过程当中就完成了服务的暴露,接下来我们着重分析这个类就可以了。
    dubbo源码分析-服务暴露流程_第1张图片
    在容器启动的过程中ServiceBean通过实现setApplicationContext时增加了一个事件监听,关注了ContextRefreshedEvent(容器刷新完毕事件)事件,一旦ContextRefreshedEvent事件传递过来,就开始调用父类(ServiceConfig)的export()方法暴露服务,而且在bean实例化之前会对应用信息、监控信息、注册中心信息等初始化。
    如下图:
    dubbo源码分析-服务暴露流程_第2张图片
    具体的服务暴露就是在ServiceConfig的export()方法中实现的。
  1. ServiceConfig#export(),这个方法首先会根据用户配置判断是否暴露,以及是否延迟暴露,不关注这块逻辑的话,最关键的部分是调用doExport()这个方法。如图:dubbo源码分析-服务暴露流程_第3张图片

  2. 紧接着进入 ServiceConfig#doExport()方法,这个方法主要是为服务暴露做一些准备工作,比如说是否已经暴露校验工作、用户配置serivice的校验工作、也包含application,registry,protocol的校验和检查工作等等,这里需要注意一点:dubbo协议默认(缺省)支持协议的是dubbo协议,在checkProtocol()这里做了处理,如图:dubbo源码分析-服务暴露流程_第4张图片
    从这个函数我们其实是可以看出dubbo默认支持dubbo协议,换个角度讲,读者如果看过官方文档的话,不难了解到dubbo是支持多协议的并且默认(缺省)协议是dubbo,官方截图如下:
    dubbo源码分析-服务暴露流程_第5张图片

  3. 在这里我要给各位读者提个醒,在阅读源码的过程中,我们要学会尽量屏蔽这些和暴露无关的细节逻辑,其实最关键的是doExport()函数的最后一段代码doExportUrls(),这个doExportUrls()才是服务暴露逻辑的延续

  4. 然后我们进入 ServiceConfig#doExportUrls()函数,如图:dubbo源码分析-服务暴露流程_第6张图片
    从这个函数的逻辑不难发现这块逻辑的功能与我们探讨doExport()函数时引申出的dubbo是支持多协议注册想呼应。简言之,上图的逻辑实现了多协议注册,是多协议支持的根本。

  5. 我们可以猜想AbstractInterfaceConfig#loadRegistries()这个函数会将用户配置的协议转换为内部的URL对象(dubbo统一数据模型)加载到一个list当中。断点进入loadRegistries()这个方法去验证下我们的想法~ 如图:dubbo源码分析-服务暴露流程_第7张图片
    这个registries就是我们提到的在容器初始化服务bean之前赋的值。分析这块源码我们可以得出这样的结论—dubbo是支持多注册中心的。当然,如果看过官方文档的,不难得出这样的结论,因为这些官网都已经说明了。dubbo源码分析-服务暴露流程_第8张图片
    我们继续往下看可以发现这个函数主要对URL相关属性的封装,loadRegistries()分析的差不多了,然后我们跳出loadRegistries(),来分析一下遍历代码中的函数ServiceConfig#doExportUrlsFor1Protocol(…);

  6. doExportUrlsFor1Protocol(…)这个函数可以理解为通过具体协议进行服务暴露。dubbo源码分析-服务暴露流程_第9张图片
    分析doExportUrlsFor1Protocol(…)的第一段逻辑不难发现这块逻辑的功能与我们探讨多协议时引申出的默认支持dubbo协议想呼应。简言之,上图的逻辑实现了默认支持dubbo协议。
    然后具体暴露服务的逻辑是在下面,如图:dubbo源码分析-服务暴露流程_第10张图片
    首先根据配置拿到服务的暴露范围,默认本地和远程都进行服务暴露。

  7. 进入 ServiceConfig#exportLocal(url)做本地暴露dubbo源码分析-服务暴露流程_第11张图片
    首先根据传入的URL对象生成一份本地暴露的URL对象,然后进入服务的暴露,服务暴露的核心代码是

Exporter<?> exporter = protocol.export(
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));

对比官方给出的暴露流程dubbo源码分析-服务暴露流程_第12张图片
          (来源于官网)
即:Invoker转换为Exporter的过程是服务暴露的核心流程。
8. proxyFactory.getInvoker(ref, (Class) interfaceClass, local)这段逻辑是具体服务转换为Invoker的过程,这个过程中用到了ProxyFactory这个概念,ProxyFactory的作用在开篇也讲过了,proxyFactory是根据spi动态获取到的,具体表现为一个动态代理类,如图:dubbo源码分析-服务暴露流程_第13张图片
实际执行getInvoker动作的对象是StubProxyFactoryWrapper,同样StubProxyFactoryWrapper在开篇也说过了,这个类是一个包装类,通过他去代理具体的ProxyFactory获取Invoker,而真正工作的其实是JavassistProxyFactory图1.11
关于为什么是JavassistProxyFactory,看下图就会明白~在这里插入图片描述
很明显,如果我们不配置动态代理方式,那么缺省是javassist。
9. 得到Invoker之后,接下来我们该马不停蹄的进行转换Exporter的过程了。protocol.export(Invoker)
这里的protocol也是根据spi获取到的一个动态代理类,如图:dubbo源码分析-服务暴露流程_第14张图片
具体执行export的是ProtocolListenerWrapper,我们先看下执行的过程(接下来是一片连续代码截图~)dubbo源码分析-服务暴露流程_第15张图片
dubbo源码分析-服务暴露流程_第16张图片
dubbo源码分析-服务暴露流程_第17张图片
dubbo源码分析-服务暴露流程_第18张图片
dubbo源码分析-服务暴露流程_第19张图片
dubbo源码分析-服务暴露流程_第20张图片
dubbo源码分析-服务暴露流程_第21张图片
dubbo源码分析-服务暴露流程_第22张图片
至此,已经一口气完成了本地暴露,我结合代码大概的说一下暴露过程中需要注意的点:
(1) ServiceConfig#exportLocal拿到并放入exporter中的是ListenerExporterWrapper
(2) 服务暴露完成后,通过new一个ListenerExporterWrapper执行了用户自定义的listeners
(3) filter链是在暴露服务过程中构建的,这块随后会拿出来系统的说一下
(4)最终调用的是InjvmProtocol的export(), 然后构建了一个InjvmExporter对象作为本地暴露对象,然后将构建的对象存入AbstractProtocol的exporterMap中,与此同时,构建的InjvmExporter中也持有一份exporterMap的引用。
(5)注意区分ServiceConfig中exporter和AbstractProtocol中exporterMap
到此,本地暴露就完结了,篇幅所限远程暴露相关内容将放到下一篇文章中单独来讨论。

文末

服务暴露总结

本章节主要介绍了服务暴露从容器启动到本地暴露完成的整个流程,spring容器启动的时候,会自动解析dubbo自定义命名空间,通过自定义的BeanDefinitionParser将dubbo内部组件生成相应的BeanDefiniton交由spring管理bean的生命周期,并在ServiceBean属性设置之后初始化之前将Application、Registry、Monitor,Protocol这些组件设置到ServiceBean中,然后在监听到spring容器初始化完成事情之后,触发服务暴露(export()),如果是一个延迟暴露的服务,那么dubbo会启动一个守护进行,在制定延迟时间到达后执行服务暴露,并且dubbo支持多协议多注册中心的暴露;在服务暴露过程中,filter链会在protocol#export的时间点去构建,自定义监听将在协议服务暴露完成,返回Exporter(类型是ListenerExporterWrapper)的时候触发。

关于交流

鉴于本人才疏学浅,谈到的内容若有不对的地方烦请告知~也期望与大家一起交流共同进步。

服务暴露时序图

最后放上一张官方的服务暴露时序图来结束本章节的介绍,如有疑问请留言~
dubbo源码分析-服务暴露流程_第23张图片

你可能感兴趣的:(dubbo,dubbo源码分析)