导航:首页 > 源码编译 > dubbo源码设置

dubbo源码设置

发布时间:2022-11-20 05:24:27

A. 如何更好地学习bbo源代码

1、Dubbo与Spring的整合 Dubbo在使用上可以做到非常简单,不管是Provider还是Consumer都可以通过Spring的配置文件进行配置,配置完之后,就可以像使用 spring bean一样进行服务暴露和调用了,完全看不到bbo api的存在。这是因为bbo使用了spring提供的可扩展Schema自定义配置支持。在spring配置文件中,可以像、这样进行配置。 META-INF下的spring.handlers文件中指定了bbo的xml解析类:DubboNamespaceHandler。像前面的被解 析成ServiceConfig,被解析成ReferenceConfig等等。 2、jdk spi扩展 由于Dubbo是开源框架,必须要提供很多的可扩展点。Dubbo是通过扩展jdk spi机制来实现可扩展的。具体来说,就是在META-INF目录下,放置文件名为接口全称,文件中为key、value键值对,value为具体实现类 的全类名,key为标志值。由于bbo使用了url总线的设计,即很多参数通过URL对象来传递,在实际中,具体要用到哪个值,可以通过url中的参 数值来指定。 Dubbo对spi的扩展是通过ExtensionLoader来实现的,查看ExtensionLoader的源码,可以看到Dubbo对jdk spi做了三个方面的扩展:
(1)jdk spi仅仅通过接口类名获取所有实现,而ExtensionLoader则通过接口类名和key值获取一个实现;
(2)Adaptive实现,就是生成一个代理类,这样就可以根据实际调用时的一些参数动态决定要调用的类了。
(3)自动包装实现,这种实现的类一般是自动激活的,常用于包装类,比如Protocol的两个实现类:ProtocolFilterWrapper、ProtocolListenerWrapper。 3、url总线设计 Dubbo为了使得各层解耦,采用了url总线的设计。我们通常的设计会把层与层之间的交互参数做成Model,这样层与层之间沟通成本比较大,扩展起来也比较麻烦。因此,Dubbo把各层之间的通信都采用url的形式。比如,注册中心启动时,参数的url为: registry://0.0.0.0:9090?codec=registry&transporter=netty 这就表示当前是注册中心,绑定到所有ip,端口是9090,解析器类型是registry,使用的底层网络通信框架是netty。
二、Dubbo启动过程
Dubbo分为注册中心、服务提供者(provider)、服务消费者(consumer)三个部分。 1、注册中心启动过程 注册中心的启动过程,主要看两个类:RegistrySynchronizer、RegistryReceiver,两个类的初始化方法都是start。 RegistrySynchronizer的start方法:
(1)把所有配置信息load到内存;
(2)把当前注册中心信息保存到数据库;
(3)启动5个定时器。 5个定时器的功能是: (1)AutoRedirectTask,自动重定向定时器。默认1小时运行1次。如果当前注册中心的连接数高于平均值的1.2倍,则将多出来的连接数重定向到其他注册中心上,以达到注册中心集群的连接数均衡。 (2)DirtyCheckTask,脏数据检查定时器。作用是:分别检查缓存provider、数据库provider、缓存consumer、数据库 consumer的数据,清除脏数据;清理不存活的provider和consumer数据;对于缓存中的存在的provider或consumer而数 据库不存在,重新注册和订阅。 (3)ChangedClearTask,changes变更表的定时清理任务。作用是读取changes表,清除过期数据。 (4)AlivedCheckTask,注册中心存活状态定时检查,会定时更新registries表的expire字段,用以判断注册中心的存活状态。如果有新的注册中心,发送同步消息,将当前所有注册中心的地址通知到所有客户端。 (5)ChangedCheckTask,变更检查定时器。检查changes表的变更,检查类型包括:参数覆盖变更、路由变更、服务消费者变更、权重变更、负载均衡变更。

B. bbo之ProtocolFilterWrapper

ProtocolFilterWrapper是bbo-rpc模块中,bbo-rpc-api的一个核心类,其中核心方法buildInvokerChain,顾名思义构建invoker链。bbo源码看到这块时,理解起来有点费劲儿,特意做记录,方便日后查看。

1、首先,我们先看一下方法中的3个核心变量,invoker、filter、next

2、可以清晰看到源码中,invoker采用了匿名类 ProtocolFilterWrapper$1 实现,我们来看一下生成的匿名内部类结构

重点关注红框内的构造方法,以及invoke方法的实现。

OK,下面我们对buildInvokerChain的具体逻辑做分析;我们先对方法逻辑做一个抽象,首先是原始方法

借助前面我们提到的匿名类,我们做一下抽象,下面是抽象后的方法:

这样看起来就简单多了,实际上这块逻辑就是把url里拿到的filter包装成Invoker,串起来;下面我们了解一下bbo是如何把Invoker 串起来的,为了方便理解,这里做图解。

假设现在有A、B、C、D、E 5个filter,初始Invoker顺序如下:

最后 return last 5,这样就把所有filter串起来了,最终的Invoker chain顺序是 last 5 -> last 4 -> last 3 -> last 2 -> last 1(即 invoker 本身)。

C. Dubbo——路由机制(下)

在 Dubbo——路由机制(上) ,介绍了 Router 接口的基本功能以及 RouterChain 加载多个 Router 的实现,之后介绍了 ConditionRouter 这个类对条件路由规则的处理逻辑以及 ScriptRouter 这个类对脚本路由规则的处理逻辑。本文继续介绍剩余的三个 Router 接口实现类。

FileRouterFactory 是 ScriptRouterFactory 的装饰器,其扩展名为 file,FileRouterFactory 在 ScriptRouterFactory 基础上增加了读取文件的能力。可以将 ScriptRouter 使用的路由规则保存到文件中,然后在 URL 中指定文件路径,FileRouterFactory 从中解析到该脚本文件的路径并进行读取,调用 ScriptRouterFactory 去创建相应的 ScriptRouter 对象。

下面来看 FileRouterFactory 对 getRouter() 方法的具体实现,其中完成了 file 协议的 URL 到 script 协议 URL 的转换,如下是一个转换示例,首先会将 file:// 协议转换成 script:// 协议,然后会添加 type 参数和 rule 参数,其中 type 参数值根据文件后缀名确定,该示例为 js,rule 参数值为文件内容。

可以再结合接下来这个示例分析 getRouter() 方法的具体实现:

TagRouterFactory 作为 RouterFactory 接口的扩展实现,其扩展名为 tag。但是需要注意的是,TagRouterFactory 与之前介绍的 ConditionRouterFactory、ScriptRouterFactory 的不同之处在于,它是通过继承 CacheableRouterFactory 这个抽象类,间接实现了 RouterFactory 接口。

CacheableRouterFactory 抽象类中维护了一个 ConcurrentMap 集合(routerMap 字段)用来缓存 Router,其中的 Key 是 ServiceKey。在 CacheableRouterFactory 的 getRouter() 方法中,会优先根据 URL 的 ServiceKey 查询 routerMap 集合,查询失败之后会调用 createRouter() 抽象方法来创建相应的 Router 对象。在 TagRouterFactory.createRouter() 方法中,创建的自然就是 TagRouter 对象了。

通过 TagRouter,可以将某一个或多个 Provider 划分到同一分组,约束流量只在指定分组中流转,这样就可以轻松达到流量隔离的目的,从而支持灰度发布等场景。

目前,Dubbo 提供了动态和静态两种方式给 Provider 打标签,其中动态方式就是通过服务治理平台动态下发标签,静态方式就是在 XML 等静态配置中打标签。Consumer 端可以在 RpcContext 的 attachment 中添加 request.tag 附加属性,注意保存在 attachment 中的值将会在一次完整的远程调用中持续传递,我们只需要在起始调用时进行设置,就可以达到标签的持续传递。

了解了 Tag 的基本概念和功能之后,再简单介绍一个 Tag 的使用示例。

在实际的开发测试中,一个完整的请求会涉及非常多的 Provider,分属不同团队进行维护,这些团队每天都会处理不同的需求,并在其负责的 Provider 服务中进行修改,如果所有团队都使用一套测试环境,那么测试环境就会变得很不稳定。如下图所示,4 个 Provider 分属不同的团队管理,Provider 2 和 Provider 4 在测试环境测试,部署了有 Bug 的版本,这样就会导致整个测试环境无法正常处理请求,在这样一个不稳定的测试环境中排查 Bug 是非常困难的,因为可能排查到最后,发现是别人的 Bug。

为了解决上述问题,我们可以针对每个需求分别独立出一套测试环境,但是这个方案会占用大量机器,前期的搭建成本以及后续的维护成本也都非常高。

下面是一个通过 Tag 方式实现环境隔离的架构图,其中,需求 1 对 Provider 2 的请求会全部落到有需求 1 标签的 Provider 上,其他 Provider 使用稳定测试环境中的 Provider;需求 2 对 Provider 4 的请求会全部落到有需求 2 标签的 Provider 4 上,其他 Provider 使用稳定测试环境中的 Provider。

在一些特殊场景中,会有 Tag 降级的场景,比如找不到对应 Tag 的 Provider,会按照一定的规则进行降级。如果在 Provider 集群中不存在与请求 Tag 对应的 Provider 节点,则默认将降级请求 Tag 为空的 Provider;如果希望在找不到匹配 Tag 的 Provider 节点时抛出异常的话,我们需设置 request.tag.force = true。

如果请求中的 request.tag 未设置,只会匹配 Tag 为空的 Provider,也就是说即使集群中存在可用的服务,若 Tag 不匹配也就无法调用。一句话总结,携带 Tag 的请求可以降级访问到无 Tag 的 Provider,但不携带 Tag 的请求永远无法访问到带有 Tag 的 Provider。

下面再来看 TagRouter 的具体实现。在 TagRouter 中持有一个 TagRouterRule 对象的引用,在 TagRouterRule 中维护了一个 Tag 集合,而在每个 Tag 对象中又都维护了一个 Tag 的名称,以及 Tag 绑定的网络地址集合,如下图所示:

另外,在 TagRouterRule 中还维护了 addressToTagnames、tagnameToAddresses 两个集合(都是 Map<String, List<String>> 类型),分别记录了 Tag 名称到各个 address 的映射以及 address 到 Tag 名称的映射。在 TagRouterRule 的 init() 方法中,会根据 tags 集合初始化这两个集合。

了解了 TagRouterRule 的基本构造之后,我们继续来看 TagRouter 构造 TagRouterRule 的过程。TagRouter 除了实现了 Router 接口之外,还实现了 ConfigurationListener 接口,如下图所示:

ConfigurationListener 用于监听配置的变化,其中就包括 TagRouterRule 配置的变更。当我们通过动态更新 TagRouterRule 配置的时候,就会触发 ConfigurationListener 接口的 process() 方法,TagRouter 对 process() 方法的实现如下:

我们可以看到,如果是删除配置的操作,则直接将 tagRouterRule 设置为 null,如果是修改或新增配置,则通过 TagRuleParser 解析传入的配置,得到对应的 TagRouterRule 对象。TagRuleParser 可以解析 yaml 格式的 TagRouterRule 配置,下面是一个配置示例:

经过 TagRuleParser 解析得到的 TagRouterRule 结构,如下所示:

除了上图展示的几个集合字段,TagRouterRule 还从 AbstractRouterRule 抽象类继承了一些控制字段,后面介绍的 ConditionRouterRule 也继承了 AbstractRouterRule。

AbstractRouterRule 中核心字段的具体含义大致可总结为如下:

我们可以看到,AbstractRouterRule 中的核心字段与前面的示例配置是一一对应的。

我们知道,Router 最终目的是要过滤符合条件的 Invoker 对象,下面我们一起来看 TagRouter 是如何使用 TagRouterRule 路由逻辑进行 Invoker 过滤的,大致步骤如下:

上述流程的具体实现是在 TagRouter.route() 方法中,如下所示:

除了之前介绍的 TagRouterFactory 继承了 CacheableRouterFactory 之外,ServiceRouterFactory 也继承 CachabelRouterFactory,具有了缓存的能力,具体继承关系如下图所示:

ServiceRouterFactory 创建的 Router 实现是 ServiceRouter,与 ServiceRouter 类似的是 AppRouter,两者都继承了 ListenableRouter 抽象类(虽然 ListenableRouter 是个抽象类,但是没有抽象方法留给子类实现),继承关系如下图所示:

ListenableRouter 在 ConditionRouter 基础上添加了动态配置的能力,ListenableRouter 的 process() 方法与 TagRouter 中的 process() 方法类似,对于 ConfigChangedEvent.DELETE 事件,直接清空 ListenableRouter 中维护的 ConditionRouterRule 和 ConditionRouter 集合的引用;对于 ADDED、UPDATED 事件,则通过 ConditionRuleParser 解析事件内容,得到相应的 ConditionRouterRule 对象和 ConditionRouter 集合。这里的 ConditionRuleParser 同样是以 yaml 文件的格式解析 ConditionRouterRule 的相关配置。ConditionRouterRule 中维护了一个 conditions 集合(List<String> 类型),记录了多个 Condition 路由规则,对应生成多个 ConditionRouter 对象。

整个解析 ConditionRouterRule 的过程,与前文介绍的解析 TagRouterRule 的流程类似。

在 ListenableRouter 的 route() 方法中,会遍历全部 ConditionRouter 过滤出符合全部路由条件的 Invoker 集合,具体实现如下:

ServiceRouter 和 AppRouter 都是简单地继承了 ListenableRouter 抽象类,且没有覆盖 ListenableRouter 的任何方法,两者只有以下两点区别。

本文我们是紧接 Dubbo——路由机制(上) 的内容,继续介绍了剩余 Router 接口实现的内容。

我们介绍了基于文件的 FileRouter 实现,其底层会依赖之前介绍的 ScriptRouter;接下来又讲解了基于 Tag 的测试环境隔离方案,以及如何基于 TagRouter 实现该方案,同时深入分析了 TagRouter 的核心实现;最后我们还介绍了 ListenableRouter 抽象类以及 ServerRouter 和 AppRouter 两个实现,它们是在条件路由的基础上添加了动态变更路由规则的能力,同时区分了服务级别和服务实例级别的配置。

D. Dubbo(一)——Dubbo 集成于 Spring 的原理

最近一直在看bbo的源码部分。在阅读的时候,需要有一个入手点,才能一点一点的进行下去。自己在研究的时候,发现思绪比较乱,于是就以 芋道源码 为基础,一点一点的啃食。芋道源码是直接从bbo的配置和一些核心的API开始讲起,是从bbo已经启动的过程作为开始节点,而这些核心 API 与 Spring 的之间的关系被省略了,这些东西对我来说属于前置的知识点,所以花了比较长的时间又从 Dubbo 的核心 API 倒着往前看。

在阅读 Dubbo 时,发现前置知识越来越多,如:Spring 的 refresh 中的一些核心点,Spring 中 bean 的生命周期,BeanFactory 与 FactoryBean 的区别等。所以这些前置知识花了特别多的时间去补。所幸,虽然补前置知识虽然时间长,但是性价比还是可以的。Dubbo 是依赖于Spring 的上下文环境的框架,其他依赖于 Spring 的框架也是相同的道理。Spring 的一些对外的扩展点,读过之后也会心中有数。

1、本篇主要是描述了 Dubbo 在 Spring 创建上下文的时候,是如何从创建,到能完整提供一个RPC调用能力的一些相关点。
2、由于源码比较多,直接贴断点也太过臃肿,所以仅仅贴一些关键点来概括整个流程。
3、本文是依赖于前面的 bbo 项目进行断点分析,项目结构可以参照这里。项目中 bbo 的配置方式是 xml 文件,所以本篇主要说 xml 配置方式。其他方式道理相同,并不是问题的关键点。

4、项目启动的是 bbo-user 服务,所以 UserService 为 bbo:service,OrderService 为 bbo:reference。

下图为Spring 启动时是如何加载 Dubbo 的,其中省略了大量过程,只保留了一些关键节点,省略的部分可以略微脑补一下。

整个流程的入口是 Spring 的 refresh 方法。每个方法都有比较深的调用栈。与 Dubbo 有关的入口是 refresh 中的 方法

这个方法是执行 beanFactory 的一些后处理操作,其核心流程为在Spring容器中找出实现了BeanFactoryPostProcessor接口的processor并执行。Spring容器会委托给的方法执行。
是比较核心的类,在这里我们关注一下这个类。它的作用是对项目中配置的类进行处理。具体处理可以分为几步:

在加载类信息时,spring 会去用各种方式扫到注册的 bean 信息。我们在 spring 中注册的 bean,逃不出这个方法的扫描方式。 核心方法是:

扫描之后,会将扫描到的 bean 注册到 beanDefinitionMap 中

首先是此处 org.springframework.beans.factory.xml.#parseBeanDefinitions,可以看出方法会以配置文件根节点起,遍历所有子节点。

其次是这里 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition), 此方法会通过解析出来的节点,获取对应的 Spring 的 namespaceUri ,进而获取对应的配置文件处理器。
此处 ele 参数实际值为 <bbo:service ... />,namespaceUri 为 http://code.alibabatech.com/schema/bbo

我们看一下 resolve 方法中的细节。因为这个方法内部才是 Dubbo 依赖于 Spring 的关键点。

此处的 NamespaceHandler 为 DubboNamespaceHandler,再创建结束之后,进行 init 初始化。

可以看到,DubboNamespaceHandler 在初始化的时候,会创建所有 bbo 标签对应的Config 类的 DubboBeanDefinitionParser。并将 DubboBeanDefinitionParser 和 对应的 bbo 标签类注册到 NamespaceHandlerSupport 的 parsers 中。

最后,会在 com.alibaba.bbo.config.spring.schema.DubboBeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, java.lang.Class<?>, boolean) 方法中进行处理

Dubbo 服务比较特殊,beanDefinition 跟普通的 bean 不太一样。在向 beanDefinitionMap 注册时,普通的 beanDefinition 的 beanName 与 beanClass 是对应的;而 bbo 服务的 beanDefinition 的 beanName 是bbo 服务的名称,beanClass 为 bbo 对应的 Bean。

普通的 beanDefinition:

bbo 引用的服务的 beanDefinition:

这一步的核心流程是从 beanFactory 中获取所有的 ApplicationListener,然后注册到监听器集合中。它的关键点其实是 ServiceBean。因为 ServiceBean 是 ApplicationListener 的实现。

所以 beanFactory 中 ServiceBean 也会被注册到监听器集合中。项目中的 ServiceBean 的 beanClass 实际是 UserService。

这一步的核心点,主要是创建剩余的各类对象,并将其保存到 singletonObjects 中。其中关联的前置知识为 Spring 中 bean 的生命周期 。它的核心方法是:
org.springframework.beans.factory.support.#doCreateBean

它的具体流程为:

ps:此处并不是只有这一步才会跟 bean 生命周期相关,bean 生命周期贯穿在 refresh 的很多流程中,只要执行doGetBean 方法,都会走这个流程。此处仅仅借楼关联一下。

这一步的核心点,是通知所有的监听器上下文刷新结束的事件。在这一步执行时,会通知到 ServiceBean。

此处暴露的是 UserService。

Dubbo 的启动条件是完全依赖于 Spring 的启动流程,Spring 的启动流程中核心的点是 refresh 方法。所以只要搞懂 refresh 方法,其他的拓展框架流程也会明白。只不过关联的知识点太多了,还是需要时间的积累才能一点一点的搞懂。
如果本篇有描述不清,或者描述有误的地方,还望在下方留言,大家一起交流,一起学习,一起进步~

E. Dubbo之SPI实现原理详解

 SPI全称为Service Provider Interface,是一种服务提供机制,比如在现实中我们经常会有这种场景,就是对于一个规范定义方而言(可以理解为一个或多个接口),具体的服务实现方是不可知的(可以理解为对这些接口的实现类),那么在定义这些规范的时候,就需要规范定义方能够通过一定的方式来获取到这些服务提供方具体提供的是哪些服务,而SPI就是进行这种定义的。

说明:



Dubbo 的扩展点加载是基于JDK 标准的 SPI 扩展点发现机制增强而来的,Dubbo 改进了 JDK 标准的 SPI 的以下问题:


bbo对于SPI的实现主要是在ExtensionLoader这个类中,这个类主要有三个方法:


如下是getExtension()方法的源码:



createExtension()方法的源码:

在createExtension()方法中,其主要做了三件事:

关于wrapper对象,这里需要说明的是,其主要作用是为目标对象实现AOP。wrapper对象有两个特点:



getExtensionClasses()方法的源码



loadDirectory()方法的源码:



loadClass()方法的源码

loadClass()方法主要作用是对子类进行划分,这里主要划分成了三部分:

总结而言,getExtension()方法主要是获取指定名称对应的子类。在获取过程中,首先会从缓存中获取是否已经加载过该子类,如果没加载过则通过定义文件加载,并且使用获取到的wrapper对象封装目标对象返回。




getAdaptiveExtension()方法源码



F. Dubbo之服务导出源码分析

要了解服务导出做了什么,需要了解导出的目的是什么?
bbo是一款面向接口代理的高性能RPC调用,说白了就是提供远程服务。

服务导出需要做的:简单说就是根据服务参数、服务协议构建服务URL,注册到注册中心,并启动Server。其中服务参数可以动态配置也需要监听。

1、onApplicationEvent
org.apache.bbo.config.spring.ServiceBean#onApplicationEvent
发布ContextRefreshedEvent,进行导出服务

2、export
org.apache.bbo.config.spring.ServiceBean#export
调用父类ServiceConfig导出,之后发布一个ServiceBeanExportedEvent

②、shouldExport检查服务是否需要导出
org.apache.bbo.config.ServiceConfig#shouldExport

org.apache.bbo.config.ServiceConfig#checkAndUpdateSubConfigs

1、completeCompoundConfigs
ServiceConfig中的某些属性如果是空的,那么就从ProviderConfig、MoleConfig、ApplicationConfig中获取
①、如果配置了ProviderConfig provider,如果application、mole、registries、monitor、protocols、configCenter这些属性为空的情况下,可以从provider中获取信息并赋值

③、如果ApplicationConfig application不为空,可以为registries、monitor为空的赋值
2、startConfigCenter
从配置中心获取配置,包括应用配置和全局配置

SystemConfiguration:是系统环境变量,可以在服务启动时通过-D指定参数
AbstractConfig:是通过@Service注解配置的参数
PropertiesConfiguration:是bbo.properties文件配置的
AppExternalConfiguration和ExternalConfiguration:是在配置中心Dubbo-Admin中配置的,AppXxx是应用级别
配置优先级是:SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
如果放到第二个位置优先级是:SystemConfiguration -->AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> -> PropertiesConfiguration
c、根据setXX()方法和setParameters()方法进行参数值覆盖

②、prepareEnvironment
管理台上的动态配置中心,如果是zookeeper,获取的就是/bbo/config/bbo/bbo.properties节点中的内容。如果是应用级别的则获取的就是/bbo/config/bbo-demo-consumer-application/bbo.properties节点中的内容(bbo-demo-consumer-application应用名,这里是以bbo-demo为例)

4、checkProtocol 从配置中心设置Protocol配置

10、Local、Stub、Mock
local和stub一样,不建议使用了。如果Local存根为true,则存根类为interfaceName + "Local"。如果Stub本地存根为true,则存根类为interfaceName + "Stub"

org.apache.bbo.config.ServiceConfig#doExport

unexported表示:当前服务已经被取消了,就不需要再导出了
exported表示:已经导出了,就不再重复导出了
1、doExportUrls
org.apache.bbo.config.ServiceConfig#doExportUrls

②、遍历protocols协议
pathKey = group/应用名/path服务名:version
例如:mygroup/bbo-demo/org.apache.bbo.demo.DemoService:1.0.1
ProviderMethodModel表示某一个方法、方法名所属的服务的,包含实现类,接口,以及接口中的各个方法

ApplicationModel表示应用中有哪些服务提供者和引用了哪些服务
每种协议都会导出一个单独的服务,并注册到各个注册中心
2、doExportUrlsFor1Protocol
org.apache.bbo.config.ServiceConfig#doExportUrlsFor1Protocol

③、methods方法参数处理

⑦、scope导出方式(scope=null进行远程导出)

生成的注册服务URL是在registryURL和export参数是服务url的拼装
b、也表示服务提供者,包括了Invoker和服务的配置。是对Invoker的包裹
c、protocol.export(wrapperInvoker)这是导出的核心,使用了SPI。
使用特定的协议来对服务进行导出,这里的协议是registry,导出成功后得到一个Exporter
由于注册地址也是服务,所以会先使用RegistryProtocol进行服务注册(服务导出),然后注册(服务导出)完了之后,再使用DubboProtocol进行导出
自适应SPI会获取url
org.apache.bbo.config.invoker.#getUrl

⑩、当存在注册中心时,是先使用Registy协议注册服务,然后在使用Http协议导出服务。而没有注册中心时,是直接使用Http协议导出服务。根据服务url,讲服务的元信息存入元数据中心MetadataReportService

RegistryProtocol被ProtocolFilterWrapper包裹,ProtocolFilterWrapper被ProtocolListenerWrapper包裹(SPI原理)

1、export
org.apache.bbo.rpc.protocol.ProtocolListenerWrapper#export
如果协议是REGISTRY_PROTOCOL(registry)则会直接走到下一个protocol实现类ProtocolFilterWrapper。
如果不是则会使用ListenerExporterWrapper处理

②、获取providerUrl 服务提供者url

③、生成overrideSubscribeUrl,与监听有关
在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider://,增加参数category=configurators&check=false
overrideSubscribeUrl是用来对动态配置监听的,需要监听的服务和监听的类型(configurators, 老版本)
一个overrideSubscribeUrl对应一个OverrideListener,用来监听overrideSubscribeUrl变化事件

④、doLocalExport导出
此时服务协议是bbo,DubboProtocol被ProtocolFilterWrapper包裹,ProtocolFilterWrapper被ProtocolListenerWrapper包裹

1、export
org.apache.bbo.rpc.protocol.ProtocolListenerWrapper#export
返回结果被ListenerExporterWrapper包裹

2、export
org.apache.bbo.rpc.protocol.ProtocolFilterWrapper#export

EchoFilter、ClassLoaderFilter、GenericFilter、ContextFilter、TraceFilter、TimeoutFilter、MonitorFilter、ExceptionFilter
返回包裹了Filter的CallbackRegistrationInvoker
3、export
org.apache.bbo.rpc.protocol.bbo.DubboProtocol#export

②、register
org.apache.bbo.registry.support.FailbackRegistry#register

在服务导出过程中,需要对动态配置中心的数据进行监听。如果发生更改,可以及时做出反映。
这里用到了Zookeeper的Watcher机制。
1、配置中心的路径与动态配置路径是不相同的,这里需要区分开来
配置中心的路径是:
①、应用:/bbo/config/bbo/org.apache.bbo.demo.DemoService/bbo.properties节点的内容
②、全局:/bbo/config/bbo/bbo.properties节点的内容
2、对于动态配置的监听有版本的差别
在2.7之前,只可以对单个服务进行监听,不可以对应用监听
/bbo/org.apache.bbo.demo.DemoService/configurators/* 只对路径名字监听,不监听内容
在2.7之后,服务和应用都可以监听
服务: /bbo/config/bbo/org.apache.bbo.demo.DemoService.configurators 节点的内容
应用: /bbo/config/bbo/bbo-demo-provider-application.configurators 节点的内容
3、监听时机RegistryProtocol#export
org.apache.bbo.registry.integration.RegistryProtocol#export

G. 4. Dubbo-executes设置解析

限制某个接口、方法使用的业务线程数
源码参见ExecuteLimitFilter, 利用信号量实现

参考:
https://blog.csdn.net/manzhen/article/details/75727860 : 文章中说的问题在2.6.1版本已修复

有个地方没搞明白:
对接口设置 executes = "10", 是整个接口只能有10个线程? 还是每个方法最多10个,如果这样,假设接口有5个方法,岂不是该接口最多50个线程?

H. bbo 中使用 ThreadLocal 隐患

在 Dubbo 中使用 ThreadLocal ,如果采用默认的设置,每次 Dubbo 调用结束,Dubbo 处理响应线程并不会被销毁, 而是归还到线程池中。而从 ThreadLocal 源码可以看出,每次我们设置的值其实会存在位于 Thread 中 ThreadLocalMap 变量中。

这就导致,下次如果 Dubbo 处理响应恰好继续使用到这个线程,该线程就能调用到上次响应中设置在 ThreadLocal 设置的值。这就引起内存泄露,可能还会导致业务上异常。其实并不止在 Dubbo 中,该案例还会发生在 web项目中,只要相关使用线程池的,都有可能发生。

bbo 默认采用单一长连接加线程池方式处理调用。

默认采取 Dispatcher=all 的分发策略,所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。

线程池在缺省配置为固定大小线程池,启动时建立线程,不关闭,一直持有。

使用 Threadlocal,我们需要注意几点:

调整线程池大小配置是

bbo.protocol.threads = 5000
调整线程池类型配置是
bbo.protocol.threadpool = cached
调整事件处理方式配置是
bbo.protocol.dispatcher = message

或者
<bbo:protocol name="bbo" dispatcher="all" threadpool="fixed" threads="100" />

I. 14. bbo源码-集群容错之MergeableCluster

在bbo官方的用户手册中,提到了使用 MergeableCluster 的场景--分组聚合:

功能示意图如下:

定义菜单接口方式:

Provider暴露服务--一个服务属于 group-hot ,一个服务属于 group-cold

笔者测试时启动了两个Provider,所以总计有四个服务,bbo-monitor监控显示如下:

Consumer调用服务:

几个重要的配置说明:

com.alibaba.bbo.rpc.cluster.Merger 文件内容如下:

核心源码在 MergeableClusterInvoker.java 中,源码如下所示:

在条件分支 if ( merger.startsWith(".") ) {} 中,有一段逻辑: method = returnType.getMethod( merger, returnType ); ,即从bbo服务接口方法返回类型即 java.util.List 中查找merger配置的方法,例如 .addAll ,我们先看一下debug过程各变量的值:

bbo源码中 method = returnType.getMethod( merger, returnType ); 调用 Method method = getMethod0(name, parameterTypes, true); ,再调用 Method res = privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates); ,最后调用 searchMethods(privateGetDeclaredMethods(true), name, parameterTypes)) ,得到最后方法匹配的核心逻辑如下:

从searchMethods()源码可知,方法匹配需要满足几个条件:

由上面的分析可知,如果要merger=".addAll"能够正常工作,那么只需要将bbo服务的返回类型改成 Collection 即可,例如:

如果 com.alibaba.bbo.rpc.cluster.Merger 文件集中方法无法满足需求,需要自定义实现,那么还是和bbo其他扩展实现一样,依赖SPI。只需要一下几步实现即可:

J. 【bbo源码】5.配置信息解析-注解版

用于把bbo.properties读到spring的environment中,

这个工作是由Spring的类来完成的.检测到某个需要注册的Bean上有@PropertySource注解,就会读该文件的配置信息,弄到environment对象的MutablePropertySources对象中。

后期会把配置信息弄到bbo 配置类中.

该注解上还有@DubboComponentScan,@EnableDubboConfig,这两个注解是bbo用注解与spring集成的核心了

该注解用@import导入了这个类

实现了ImportBeanDefinitionRegistrar接口,那么spring在实例化的时候会调用重写ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法,并且将用@Import导入的类的元数据包装成importingClassMetadata对象。

其实就是为了获取入口类AnnoBean上的@EnableDubboConfig注解里的multiple属性配置的值,默认是true.

然后注册了两个DubboConfigConfiguration的内部类

通过读Class对象注册到ioc容器

类上有@EnableDubboConfigBindings,值为@EnableDubboConfigBinding数组
通过绑定,将有对应前缀的配置信息赋值到对应的配置类中

又用@Import导入DubboConfigBindingsRegistrar类,DubboConfigBindingsRegistrar这个类又实现了ImportBeanDefinitionRegistrar,EnvironmentAware接口

实现ImportBeanDefinitionRegistrar肯定是为了另外导入一些类,并且拿到导入的源类,获取源类上配置的信息

实现EnvironmentAware是为了拿到spring的environment对象,因为 bbo.properties 已经被@PropertySource注解机制加载到了environment.MutablePropertySources中,在这里只对beanName的创建有作用。

registrar.registerBeanDefinitions :

注册的过程中,需要从environment对象中拿bbo相关的配置,比如ApplicationConfig只拿
bbo.application.*相关的配置,然后创建ApplicationConfig的BeanDefinition.

如果 @EnableDubboConfigBinding配置的multiple为true(默认为false),并且在配置文件中配置了同样前缀的属性,如:

这样会为同一种配置类型,生成两个BD.beanName不同的配置Bean,名称规则如下所示, #0表示的是'.'在配置的key中出现的位置

之后还会注册一个BeanPostProcessor类型的类的beanDefinition,BeanPostProcessor类型 会在每一个Bean实例化的过程中,根据配置的前缀,从environment拿出所需的配置,根据beanName来处理beanName相同的这一个配置Bean,把配置信息绑定到配置类的属性中。

.

利用 bboConfigBinder 对象来绑定前缀为bbo.application的配置信息到配置Bean中

这里bboConfigBinder对象是中的一个属性,是在因为这个类实现了InitializingBean这个接口的afterPropertiesSet方法,bboConfigBinder对象就是在这里初始化的

最后用的DataBinder的api把一个MutablePropertyValues绑定到Bean的属性

@import进来了DubboComponentScanRegistrar类

DubboComponentScanRegistrar又实现了ImportBeanDefinitionRegistrar接口,实现registerBeanDefinitions方法.

跟xml的逻辑一样,同样是

阅读全文

与dubbo源码设置相关的资料

热点内容
linuxoffice2016 浏览:670
小宇宙app怎么付费 浏览:375
同花顺上传到服务器地址 浏览:929
电脑加密安卓版 浏览:824
手机程序加密有什么作用 浏览:178
求黑马程序员python教程 浏览:528
androidmvvm优缺点 浏览:894
unix下编译库文件 浏览:633
程序员的u盘 浏览:237
android根据经纬度获取城市 浏览:564
python使用解释器还是编译器 浏览:358
以下关于有加密算法及密钥描述 浏览:220
linuxgethostname 浏览:416
程序员多数有对象 浏览:131
单片机延时程序计算 浏览:444
编译原理语法翻译 浏览:504
pr编译出错渲染存在偏移 浏览:262
如何制作自家的app 浏览:199
推荐一个解压软件rar解压帮手 浏览:212
wd文档加密器 浏览:748