Dubbo之SPI实现原理详解
SPI全称为Service Provider Interface,是一种服务提供机制,比如在现实中我们经常会有这种场景,就是对于一个规范定义方而言(可以理解为一个或多个接口),具体的服务实现方是不可知的(可以理解为对这些接口的实现类),那么在定义这些规范的时候,就需要规范定义方能够通过一定的方式来获取到这些服务提供方具体提供的是哪些服务,而SPI就是进行这种定义的。
说明:
Dubbo 的扩展点加载是基于JDK 标准的 SPI 扩展点发现机制增强而来的,Dubbo 改进了 JDK 标准的 SPI 的以下问题:
dubbo对于SPI的实现主要是在ExtensionLoader这个类中,这个类主要有三个方法:
如下是getExtension()方法的源码:
createExtension()方法的源码:
在createExtension()方法中,其主要做了三件事:
关于wrapper对象,这里需要说明的是,其主要作用是为目标对象实现AOP。wrapper对象有两个特点:
getExtensionClasses()方法的源码
loadDirectory()方法的源码:
loadClass()方法的源码
loadClass()方法主要作用是对子类进行划分,这里主要划分成了三部分:
总结而言,getExtension()方法主要是获取指定名称对应的子类。在获取过程中,首先会从缓存中获取是否已经加载过该子类,如果没加载过则通过定义文件加载,并且使用获取到的wrapper对象封装目标对象返回。
getAdaptiveExtension()方法源码
dubbo原理和机制:应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成。
注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。
监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示。
服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销。
服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销。
扩展资料
在大规模dubbo服务化之前,应用可能只是通过RMI或Hessian等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过F5等硬件进行负载均衡。
(1)当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。
此时需要一个服务注册中心,动态的注册和发现服务,使服务的位置透明。
并通过在消费方获取服务提供方地址列表,实现软负载均衡和Failover,降低对F5硬件负载均衡器的依赖,也能减少部分成本。
(2)当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。
这时,需要自动画出应用间的依赖关系图,以帮助架构师理清理关系。
(3)接着,服务的调用量越来越大,服务的容量问题就暴露出来。
为了解决这些问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。
其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阀值,记录此时的访问量,再以此访问量乘以机器数反推总容量。
最近一直在看dubbo的源码部分。在阅读的时候,需要有一个入手点,才能一点一点的进行下去。自己在研究的时候,发现思绪比较乱,于是就以 芋道源码 为基础,一点一点的啃食。芋道源码是直接从dubbo的配置和一些核心的API开始讲起,是从dubbo已经启动的过程作为开始节点,而这些核心 API 与 Spring 的之间的关系被省略了,这些东西对我来说属于前置的知识点,所以花了比较长的时间又从 Dubbo 的核心 API 倒着往前看。
在阅读 Dubbo 时,发现前置知识越来越多,如:Spring 的 refresh 中的一些核心点,Spring 中 bean 的生命周期,BeanFactory 与 FactoryBean 的区别等。所以这些前置知识花了特别多的时间去补。所幸,虽然补前置知识虽然时间长,但是性价比还是可以的。Dubbo 是依赖于Spring 的上下文环境的框架,其他依赖于 Spring 的框架也是相同的道理。Spring 的一些对外的扩展点,读过之后也会心中有数。
1、本篇主要是描述了 Dubbo 在 Spring 创建上下文的时候,是如何从创建,到能完整提供一个RPC调用能力的一些相关点。
2、由于源码比较多,直接贴断点也太过臃肿,所以仅仅贴一些关键点来概括整个流程。
3、本文是依赖于前面的 dubbo 项目进行断点分析,项目结构可以参照这里。项目中 dubbo 的配置方式是 xml 文件,所以本篇主要说 xml 配置方式。其他方式道理相同,并不是问题的关键点。
4、项目启动的是 dubbo-user 服务,所以 UserService 为 dubbo:service,OrderService 为 dubbo:reference。
下图为Spring 启动时是如何加载 Dubbo 的,其中省略了大量过程,只保留了一些关键节点,省略的部分可以略微脑补一下。
整个流程的入口是 Spring 的 refresh 方法。每个方法都有比较深的调用栈。与 Dubbo 有关的入口是 refresh 中的 invokeBeanFactoryPostProcessors 方法
这个方法是执行 beanFactory 的一些后处理操作,其核心流程为在Spring容器中找出实现了BeanFactoryPostProcessor接口的processor并执行。Spring容器会委托给PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法执行。
ConfigurationClassPostProcessor 是比较核心的类,在这里我们关注一下这个类。它的作用是对项目中配置的类进行处理。具体处理可以分为几步:
在加载类信息时,spring 会去用各种方式扫到注册的 bean 信息。我们在 spring 中注册的 bean,逃不出这个方法的扫描方式。 核心方法是:
扫描之后,会将扫描到的 bean 注册到 beanDefinitionMap 中
首先是此处 orgspringframeworkbeansfactoryxmlDefaultBeanDefinitionDocumentReader#parseBeanDefinitions,可以看出方法会以配置文件根节点起,遍历所有子节点。
其次是这里 orgspringframeworkbeansfactoryxmlBeanDefinitionParserDelegate#parseCustomElement(orgw3cdomElement, orgspringframeworkbeansfactoryconfigBeanDefinition), 此方法会通过解析出来的节点,获取对应的 Spring 的 namespaceUri ,进而获取对应的配置文件处理器。
此处 ele 参数实际值为 <dubbo:service />,namespaceUri 为 http://codealibabatechcom/schema/dubbo
我们看一下 resolve 方法中的细节。因为这个方法内部才是 Dubbo 依赖于 Spring 的关键点。
此处的 NamespaceHandler 为 DubboNamespaceHandler,再创建结束之后,进行 init 初始化。
可以看到,DubboNamespaceHandler 在初始化的时候,会创建所有 dubbo 标签对应的Config 类的 DubboBeanDefinitionParser。并将 DubboBeanDefinitionParser 和 对应的 dubbo 标签类注册到 NamespaceHandlerSupport 的 parsers 中。
最后,会在 comalibabadubboconfigspringschemaDubboBeanDefinitionParser#parse(orgw3cdomElement, orgspringframeworkbeansfactoryxmlParserContext, javalangClass<>, boolean) 方法中进行处理
Dubbo 服务比较特殊,beanDefinition 跟普通的 bean 不太一样。在向 beanDefinitionMap 注册时,普通的 beanDefinition 的 beanName 与 beanClass 是对应的;而 dubbo 服务的 beanDefinition 的 beanName 是dubbo 服务的名称,beanClass 为 dubbo 对应的 Bean。
普通的 beanDefinition:
dubbo 引用的服务的 beanDefinition:
这一步的核心流程是从 beanFactory 中获取所有的 ApplicationListener,然后注册到监听器集合中。它的关键点其实是 ServiceBean。因为 ServiceBean 是 ApplicationListener 的实现。
所以 beanFactory 中 ServiceBean 也会被注册到监听器集合中。项目中的 ServiceBean 的 beanClass 实际是 UserService。
这一步的核心点,主要是创建剩余的各类对象,并将其保存到 singletonObjects 中。其中关联的前置知识为 Spring 中 bean 的生命周期 。它的核心方法是:
orgspringframeworkbeansfactorysupportAbstractAutowireCapableBeanFactory#doCreateBean
它的具体流程为:
ps:此处并不是只有这一步才会跟 bean 生命周期相关,bean 生命周期贯穿在 refresh 的很多流程中,只要执行doGetBean 方法,都会走这个流程。此处仅仅借楼关联一下。
这一步的核心点,是通知所有的监听器上下文刷新结束的事件。在这一步执行时,会通知到 ServiceBean。
此处暴露的是 UserService。
Dubbo 的启动条件是完全依赖于 Spring 的启动流程,Spring 的启动流程中核心的点是 refresh 方法。所以只要搞懂 refresh 方法,其他的拓展框架流程也会明白。只不过关联的知识点太多了,还是需要时间的积累才能一点一点的搞懂。
如果本篇有描述不清,或者描述有误的地方,还望在下方留言,大家一起交流,一起学习,一起进步~
假设zookeeper安装在17216222这台服务器上,现在我们通过命令行查看dubbo在zookeeper注册服务的生产者和消费者信息
首先通过命令切换到/zookeeper-3411/bin目录,然后输入
(2182为zookeeper在服务器上提供服务的端口)会看到如下截图:
然后在命令行再输入:
查看目录信息,就能看到注册的dubbo服务,截图如下:
在命令行依次输入:
会看到dubbo服务提供的对外接口,截图如下:
查看消费者命令:
会看到消费者的信息,截图如下:
查看生产者命令:
会看到生产者的信息,截图如下:
0条评论