Dubbo 服务引用
0. 概述
Dubbo 服务引用的时机有两个,第一个是在 Spring 容器调用 ReferenceBean 的 afterPropertiesSet 方法时引用服务,第二个是在 ReferenceBean 对应的服务被注入到其他类中时引用。这两个引用服务的时机区别在于,第一个是饿汉式的,第二个是懒汉式的。默认情况下,Dubbo 使用懒汉式引用服务。如果需要使用饿汉式,可通过配置 dubbo:reference 的 init 属性开启。下面我们按照 Dubbo 默认配置进行分析,整个分析过程从 ReferenceBean 的 getObject 方法开始。当我们的服务被注入到其他类中时,Spring 会第一时间调用 getObject 方法,并由该方法执行服务引用逻辑。按照惯例,在进行具体工作之前,需先进行配置检查与收集工作。接着根据收集到的信息决定服务用的方式,有三种,第一种是引用本地 (JVM) 服务,第二是通过直连方式引用远程服务,第三是通过注册中心引用远程服务。不管是哪种引用方式,最后都会得到一个 Invoker 实例。如果有多个注册中心,多个服务提供者,这个时候会得到一组 Invoker 实例,此时需要通过集群管理类 Cluster 将多个 Invoker 合并成一个实例。合并后的 Invoker 实例已经具备调用本地或远程服务的能力了,但并不能将此实例暴露给用户使用,这会对用户业务代码造成侵入。此时框架还需要通过代理工厂类 (ProxyFactory) 为服务接口生成代理类,并让代理类去调用 Invoker 逻辑。避免了 Dubbo 框架代码对业务代码的侵入,同时也让框架更容易使用。
1. 服务引用时序图
官方时序图如下:
2. 源码分析
本文是基于2.7.8源码进行分析的。
ReferenceBean.afterPropertiesSet
ReferenceBean.afterPropertiesSet 是创建代理对象的入口。
- 调用getObject()方法
- get()
ReferenceConfig.init
- 设置接口信息
- 加载配置的所有注册中心,拼装成urls
- 遍历registryUrls,添加监控中心url,添加引用服务url’
- 这里以RegistryProtocol为例。调用refer方法,返回invoker对象。特别注意,这里实际上注入的是InterfaceCompatibleRegistryProtocol实例对象
RegistryProtocol.refer
- 首先为 url 设置协议头,然后根据 url 参数加载注册中心实例。
- 然后获取 group 配置,根据 group 配置决定 doRefer 第一个参数的类型。
- 然后调用doRefer方法。注意这里调用的是InterfaceCompatibleRegistryProtocol.doRefer方法
InterfaceCompatibleRegistryProtocol.doRefer
注入doRefer(Cluster cluster, Registry registry, Class type, URL url)方法中。Registry:ListenerRegistryWrapper;Cluster:MockClusterWrapper
- getInvoker()。
- getServiceDiscoveryInvoker()。
- 创建MigrationInvoker对象
- 调用interceptInvoker
RegistryProtocol.getInvoker
注意RegistryProtocol.getInvoker.createDirectory调用的是InterfaceCompatibleRegistryProtocol.createDirectory()方法。
所以创建出来的是RegistryDirectory类型对象。
- 创建一个 RegistryDirectory 实例,然后生成服务者消费者链接,并向注册中心进行注册。
- 注册完毕后,紧接着订阅 providers、configurators、routers 等节点下的数据。
- 完成订阅后,RegistryDirectory 会收到这几个节点下的子节点信息。
- 由于一个服务可能部署在多台服务器上,这样就会在 providers 产生多个节点,这个时候就需要 Cluster 将多个服务节点合并为一个,并生成一个 Invoker。
ListenerRegistryWrapper.register
向zookeeper注册consumerurl。这里以ZookeeperRegistry为例。
ListenerRegistryWrapper.registery 方法中调用ZookeeperRegistry.registry方法。
FailbackRegistry.register
- 由于 ZookeeperRegistry是继承于FailbackRegistry的,因此ZookeeperRegistry.registry方法实际调用的是FailbackRegistry.registry方法。
- FailbackRegistry 实现 AbstractRegistry 抽象类,支持失败重试的 Registry 抽象类。AbstractRegistry 进行的注册、订阅等操作,更多的是修改状态,而无和注册中心实际的操作。FailbackRegistry 在 AbstractRegistry 的基础上,实现了和注册中心实际的操作,并且支持失败重试的特性。
- FailbackRegistry.register 中调用了 ZookeeperRegistry.doRegister方法,进行注册。
RegistryDirectory.subscribe
- RegistryDirectory.subscribe 实际上最终调用的是FailbackRegistry.subscribe方法。
- FailbackRegistry.subscribe实际上是调用了ZookeeperRegistry.doSubscribe方法。
- 首次全量数据获取完成时,调用
RegistryDirectory.notify(...)
方法,回调 NotifyListener。服务消费者可创建所有的 Invoker 对象,用于调用服务提供者们。
RegistryDirectory.notify
- RegistryDirectory 是一个动态服务目录,会随注册中心配置的变化进行动态调整。因此 RegistryDirectory 实现了 NotifyListener 接口,通过这个接口获取注册中心变更通知。
- RegistryDirectory.notify 会有以下的引用链:
DubboProtocol.refer
- DubboProtocol.refer会先经过ProtocolListenerWrapper和ProtocolFilterWrapper构建监听器链和过滤器链。
- 根据url获取ExchangeClient对象,构建消费者和提供者的底层通信链接。
- 创建DubboInvoker,它包含对远程提供者的长链接,从而可以真正执行远程调用。并返回给目录服务RegistryDirectory持有。
3. 总结
本篇文章主要介绍了服务引用的过程。
4. 鸣谢
服务引用
Dubbo 服务引用源码简介