最新动态
Dubbo源码深入学习-Dubbo服务引入原理
2024-12-31 02:13

我们在平常开发中使用 dubbo 是需要从注册中心拉取服务信息的,然后进行数据组装路由过滤负载均衡发起网络请求一系列步骤进行远程调用,这一系列操作对于我们来说如同黑盒,所以我们在平时开发之余,也需要对dubbo消费者如何进行服务导入有所了解,本篇我们就一块对 dubbo 服务导入的原理进行探索

依据之前springDubbo的整合原理,我们知道,当通过@Reference 注解引入一个Dubbo服务的时候,会生成一个ReferenceBean代理对象,然后对属性进行赋值代理对象ReferenceBean是一个FactoryBean,重写FactoryBeangetObject方法会返回一个代理对象并交给Spring管理,那么ReferenceBean是如何创建这个代理对象的,咱们就来看看这个ReferenceBean的getObject方法。

 
 

根据前置知识,我们知道服务消费端创建代理对象会调用ReferenceBean的父类ReferenceConfig的get方法

 
 

checkAndUpdateSubConfigsServiceBean那里一样,检查和更新参数,把ReferenceBean里的属性的值更新为优先级最高的参数值,接着会调用init方法创建动态代理对象,并付给ref属性,然后返回ref。

init()是消费端创建动态代理对象的入口

 

init方法中,主要做了

1.把读取到的配置都放在一个map中,后续根据这个map中的参数去从注册中心查找服务

2.调用createProxy方法生成代理对象

createProxy方法中,会获取注册中心配置URL,代码如下

 

这里获取所有的注册中心的URL,并把配置信息的map也加到注册中心URL中,接下来就是一个分支逻辑:

1.如果消费者配了一个注册中心:

 

那么直接调用Protocol的refer(interfaceClass, urls.get(0));得到一个Invoker对象。

2.如果消费者配了多个注册中心:

 

遍历每个注册中心,每次调用Protocolrefer(interfaceClass, url);得到一个Invoker对象添加到invokers,然后把invokers调用CLUSTER.join(new StaticDirectory(u, invokers))把所有invokers进行封装得到一个invoker,这里是通过SPI机制,使用RegistryAwareCluster的join方法实现的,会把invoker对象封装成RegistryAwareClusterInvoker,在服务调用的时候,会遍历所有的注册中心的invoker,只要有一个是可用的就直接返回。

 

最后在createProxy方法的最后,根据最终得到的invoker对象调用PROXY_FACTORY.getProxy(invoker);得到一个代理对象,并返回,这个代理对象就是ref

1.2.1 Invoker对象的生成

每个注册中心都会调用REF_PROTOCOL.refer(interfaceClass, url)生成一个Invoker对象,我们就看看是如何生成Invoker对象的。

refer方法有两个参数:

type:表示引入的服务

url:注册中心URL(包含我们配置信息生成的url)

REF_PROTOCOL有两个包装类,在调用最终的protocol类之前,会经过两次包装类的refer方法,进行AOP增强

1.ProtocolFilterWrapper

 

这里用于添加dubbo protocol对应的invoker的过滤器链,自己实现的Filter扩展类就是在这里加载的,服务调用的时候会详讲,这里是registry protocol,所以只会走进第一个if中。

2.ProtocolListenerWrapper

 

这里用于给dubbo protocol对应的invoker添加监听器,用于处理结果,这里可以进行扩展,这里是registry protocol,所以只会走进第一个if中。

最终会调用RegistryProtocol#refer方法

 

refer方法主题逻辑就是

1.把注册URL的协议换为zookeeper,方便拿到zk的具体实现类

2.调用doRefer方法

1.2.3 创建动态目录

继续调用====

 

在一个服务目录中包含了

  1. serviceType:表示服务接口
  2. serviceKey:表示引入的服务key,serviceclass+version+group
  3. queryMap:表示引入的服务的参数配置
  4. configurators:动态配置信息
  5. routerChain:路由链
  6. invokers:表示服务目录当前缓存的服务提供者Invoker
  7. ConsumerConfigurationListener:监听本应用的动态配置
  8. ReferenceConfigurationListener:监听所引入的服务的动态配置

在这里的主逻辑为

1.创建一个动态服务目录

2.生成消费者URL并注册到注册中心

3.构建路由链,为后续消费端进行服务路由做准备

4.注册监听并从注册中心拉取配置信息以及服务提供者URL

5.生成invoker对象

1.2.3 构建路由链

RegistryProtocol.doRefer方法的directory.buildRouterChain(subscribeUrl),会根据消费者URL构建路由链

 

接着看RouterChain.buildChain(url),会调用RouterChain的构造方法

 

1.获取RouterFactory接口的所有实现类
// 0 = {MockRouterFactory@2880}
// 1 = {TagRouterFactory@2881} // 标签路由
// 2 = {AppRouterFactory@2882} // 应用条件路由
// 3 = {ServiceRouterFactory@2883} // 服务条件路由

2.调用每个Factory的getRouter方法,应用条件路径和服务条件路由相似,以AppRouterFactory为例

创建路由会来到AppRouterFactory.createRouter方法

 

DynamicConfiguration.getDynamicConfiguration()代表拿到配置中心实例,url代表消费者URL

AppRouter继承了ListenableRouter,这里会来到ListenableRouter的构造方法

 

调用ListenableRouter.init方法

 

这里会绑定监听并主动读取一次路由信息,读取完路由信息,生成的规则会赋值给其父类的成员变量。

服务路由和应用路由类似,这里就不解读了,至于标签路由的监听是在读取服务提供者URL信息的时候进行监听的,可以继续往下看。

1.2.4 监听节点拉取配置

RegistryProtocol.doRefer方法的上边这块代码,会进行消费端的监听器的注册以及主动拉取注册中心服务提供者的信息。

 

CONSUMER_CONFIGURATION_LISTENER和ReferenceConfigurationListener和生产者那一块的逻辑是一样的,都是新版本的监听器,在构造方法中调用initWith方法进行监听器的绑定以及首次读取服务的应用级别和服务级别的动态配置信息

// 当前所引入的服务的消费应用目录:/dubbo/config/dubbo/dubbo-demo-consumer-application.configurators
// 当前所引入的服务动态配置目录:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService:1.1.1:g1.configurators

而 registry.subscribe(url, this)负责的是监听老版本动态配置目录的信息以及主动从注册中心拉取服务提供者URL信息。

这里的registry是ZookeeperRegistry,它会调用父类的subscribe方法,最终会来到自己的doSubscribe方法,下边是进行注册监听的代码

 

可以看到在注册的时候,手工的调用notify方法拉取配置,这里会来到RegistryDirectory.notify(List urls

 

在这里会读取一下三个目录的信息,这里主要是读取服务提供者配置信息,会调用RegistryDirectory.refreshOverrideAndInvoker方法

“/dubbo/org.apache.dubbo.demo.DemoService/providers”
“/dubbo/org.apache.dubbo.demo.DemoService/configurators”
“/dubbo/org.apache.dubbo.demo.DemoService/routers”

 

overrideDirectoryUrl主要是根据动态配置信息重新动态目录URL信息,我们重点看refreshInvoker方法,这里会把服务提供者URL传到方法中去。

refreshInvoker方法中,会调用toInvokers方法,生成inwoker对象,这个方法是重点,主要逻辑有:

1.服务提供者可能有多个,可能是dubbo协议也有可能是rest协议,如果消费者配置了具体的协议,先根据协议过滤掉一些服务提供者URL

2.调用mergeUrl对服务提供者进行重写,主要包括消费者本身的配置信息,动态配置信息对服务提供者URL进行重写覆盖

3.判断当前服务提供者是否已经生成invoker对象,如果没有,调用==invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl)==生成一个Dubbo Invoker对象,这里根据url会调用DubboProtocol类的refer方法,再次之前会先调用wrapper类,进行AOP增强。

4.toInvokers调用完毕,生成一个map,key是我们服务提供者的url,value就是生成的invoker

然后把生成的invoker集合设置到路由链中,并赋值给动态目录的属性invokers,注意这里的invoker都是dubbo invoker 代表的是一个服务的多个提供者。

5.监听标签路由信息

在routerChain.setInvokers(newInvokers)放中,除了把新生成的invoker赋值给路由链,在这一过程中还有进行标签路由配置信息的监听,为什么要在这里监听呢?因为标签路由是针对服务提供者打标签,而我们消费者引入服务的时候是不知道服务的应用信息的,所以只能从服务提供者URL中获取到服务应用信息,然后再进行监听。

 

在这里会遍历所有的路由规则,调用notify方法,其实只有标签路由实现了这个方法,我们再看下TagRouter.notify方法

 

1.2.5 生成DubboInvoker对象

在监听拉取配置的时候,在RegistryDirectory.toInvokers方法中会拿到最终更新的服务提供者URL生成一个Invoker对象

 

url这里是根据不同的服务提供者协议,调用不同的protocol类,比如我们服务提供者使用的是dubbo协议,那么就会调用==,但是我们之前分析过是有包装的类的,所以这里会依次调用=>=>,

我们重点说下

 
 

会给DubboProtocol的url对应的invoker添加Filter过滤器,这些过滤器在服务调用的时候会执行对应的方法,这里我们先简单了解一下即可。

接着就是调用方法,但是DubboProtol没有实现refer方法,会调用其父类

 

protocolBindingRefer是由DubboProtol实现的

 

invoker在层层包装下会形成一下结构

而且在生成DubboInvoker的时候,就会去创建一个NettyClient,和服务端建立Socket连接,主要方法在getClients(url)方法上。

1.2.6 构建传输链路建立Netty连接

在Dubbo协议中, 是基于Netty进行数据传输的,生产者和消费者是可以互相传输数据的,Dubbo在此之上抽象了一个数据交换层,用于区分请求和响应

我们继续在创建DubboInvoker,追踪

 

真正创建我们交换层client的代码在client = Exchangers.connect(url, requestHandler); // connect

 

这里是调用getExchanger方法根据SPI机制获取Exchanger接口的扩展点拿到具体实现,默认是HeaderExchanger,我们继续追踪HeaderExchanger的connect方法

 

DecodeHandler把handler(ExchangeHandlerAdapter)包装两层,接着Transporters.connect来创建Client连接对象

 

调用完getTransporter().connect(url, handler)方法,会把NettyClient作为参数返回,然后调用HeaderExchangeClient的构造方法,同时开启重连任务和心跳检测任务。

 

至此我们服务导出链路走完

1.2.7 获取最终invoker对象

我们重新回到

 

还有重要的一步cluster.join(directory),把我们的服务目录作为参数,调用join方法,cluster是一个接口,默认实现为FailoverCluster,还有一个包装类型的MockClusterWrapper

FailoverCluster.join

 

MockClusterWrapper.join

 

所以我们最终返回的invoker对象是这样的结构

MockClusterInvoker

​ invoker->FailoverClusterInvoker

​ directory->

​ List DubboInvoer集合

1.2.8 根据invoker创建动态对象

 
 

现在做个小结,总结一下服务引入到底都做了那些事情

1.扫描加了@Reference注解的地方,为其进行属性注入

2.根据@Reference注解的信息,生成一个ReferenceBean对象

3.ReferenceBean是一个Factory,会通过getObject创建一个代理对象

4.创建代理对象需要获取一个Invoker对象,其创建过程为:

调用

1.创建动态目录

2.构建路由链

3.注册监听并拉取配置

4.根据服务提供者URL生成DubboInvoker对象

5.构建Netty客户端,并和Server建立连接

    以上就是本篇文章【Dubbo源码深入学习-Dubbo服务引入原理】的全部内容了,欢迎阅览 ! 文章地址:http://ww.kub2b.com/news/16569.html
     栏目首页      相关文章      动态      同类文章      热门文章      网站地图      返回首页 企库往资讯移动站 http://ww.kub2b.com/mobile/ , 查看更多   
最新文章
环球圆桌对话:用反制告诉美方,霸道高关税是错的
编者按:近日,美国借“对等关税”的名义挑动全球范围的“关税战”,引起国际舆论关注。中国为什么必须就“对等关税”实施反制?
OPPO、ViVO、加多宝的品牌营销强在哪里?oppo手机是哪个国家的品牌「OPPO、ViVO、加多宝的品牌营销强在哪里?」
今天跟大家分享品牌营销,它有规律可循。▌一、营销的品牌导向1.企业的品牌导向:创业的时候,开始的时候是产品导向,还是品牌导
手机静态ip设置参数 这七步帮你完成手机静态ip「手机静态ip设置参数 这七步帮你完成」
手机在我们现在飞速发展的社会中有着十分重要的作用,随着互联网的发展,手机的速度也是越来越快,越来越流畅。但也有时候我们在
tplogin重新设置密码,tplogincn路由器设置管理密码是多少tplogincn手机登录「tplogin重新设置密码,tplogincn路由器设置管理密码是多少」
tplogincn路由器路由器的管理密码:1.一般路由器的管理账号和密码是:admin(小写字母)。2.有些路由器要求安全登录一次,并设置自己
vivo 是什么手机牌子?认识一款手机-VIVOvivo中文叫什么手机「vivo 是什么手机牌子?认识一款手机-VIVO」
vivo,一个从音乐手机起步,逐渐成长为全球知名品牌,在智能手机领域不断追求创新和完美的品牌。从最初的步步高音乐手机,到如今
游戏手机的自我救赎:ROG 8 Pro上手后,我看到了ROG的未来专门打游戏的手机「游戏手机的自我救赎:ROG 8 Pro上手后,我看到了ROG的未来」
来源|锚思科技作者|陈宝玉 游戏手机二选一,告诉你我的选择!!! 游戏手机作为手机的一个细分产品线,只有专业玩家和对游戏有
battery guru最新版 v2.3.13手机电池检测软件「battery guru最新版 v2.3.13」
battery guru最新版是一款能够对你安卓设备的电池进行保护,能够延长其使用寿命。多项功能的设置,让你能够通过更为精准的数据,
CBA1/4决赛:辽篮拿到赛点,青岛队扳平比分
4月15日,2024-2025赛季中国男子篮球职业联赛(CBA)季后赛四分之一决赛继续进行,首回合失利的青岛队客场大胜广厦队将总比分扳
单场0分又被雪藏!火箭队第18人恐难留队,三分精准,但功能单一
火箭队季后赛的对手已然确定。北京时间4月16号,孟菲斯灰熊队客场不敌勇士队。如此一来,灰熊队还得与独行侠以及国王队的胜者进
《刺客信条:奥德赛》v1.5.0十四项修改器[MrAntiFun][Epic]刺客信条手机版下载「《刺客信条:奥德赛》v1.5.0十四项修改器[MrAntiFun][Epic]」
《刺客信条:奥德赛》v1.5.0十四项修改器,包含无限肾上腺素,无限技能点,完美潜行等等功能助你轻松“暗杀”!让你在希腊尽情无