Dubbo2 巩固教程之四
大纲
- Dubbo 2 巩固教程之一、Dubbo 2 巩固教程之二、Dubbo 2 巩固教程之三
- Dubbo 2 巩固教程之四、Dubbo 2 巩固教程之五、Dubbo 2 巩固教程之六
- Dubbo 2 巩固教程之七
前言
学习资源
Dubbo 深入理解
服务引用的源码剖析
版本说明
| 组件 | 版本 | 说明 |
|---|---|---|
| Dubbo | 2.6.6 | 在阅读 Dubbo 源码时要注意,不同版本由于协议升级、SPI 改动、注册中心增强等因素,底层实现会出现细微差异;版本跨度越大,差异通常越明显。 |
学习内容
- 配置检查与加载
- 处理直连(P2P)引用 URL
- 多注册中心处理与 URL 组装
- Invoker 的创建流程(核心类是
RegistryProtocol)- (1) 连接注册中心
- (2) 注册消费者节点
- (3) 订阅提供者、配置覆盖、路由信息
- (4) 处理多个服务提供者
- (5) 本地缓存 Invoker 实例
- (6) 返回代理工厂创建的服务代理对象
服务引用的流程
(1) 读取配置
- Spring 启动 → 解析
@DubboReference/<dubbo:reference>→ 生成 ReferenceBean。
- Spring 启动 → 解析
(2) 初始化 ReferenceBean
- 触发
afterPropertiesSet()→ 准备接口名、协议、直连 URL、注册中心等信息。
- 触发
(3) 发起引用
- 调用
ReferenceConfig.get()→ 开始构建 Invoker。
- 调用
(4) 处理直连(可选)
- 若配置了
url=xxx→ 直接构造 Invoker,不走注册中心(Registry)。
- 若配置了
(5) 注册中心引用
- 若使用注册中心(Registry) → 调用
RegistryProtocol.refer():- 连接注册中心
- 注册消费者的 URL
- 消费者订阅
providers/routers/configurators这三类节点
- 若使用注册中心(Registry) → 调用
(6) 构建集群 Invoker
- 多个 Provider → 创建多个 Invoker → 通过 Cluster(比如
FailoverCluster)合并为一个 ClusterInvoker。
- 多个 Provider → 创建多个 Invoker → 通过 Cluster(比如
(7) 创建代理对象
ProxyFactory.getProxy(ClusterInvoker)→ 返回接口代理实例(比如,使用字节码操作技术 Javassist)。

服务消费者的配置
- Dubbo 服务消费者的配置信息如下图所示:

- Dubbo 服务消费者配置检查与加载的方法是:
ReferenceConfig.init()
服务引用的入口方法
- Dubbo 服务引用的入口方法是
ReferenceConfig.get(),可以从这个方法入手阅读 Dubbo 服务引用的底层源码实现
1 | ReferenceConfig<OrderService> reference = new ReferenceConfig<OrderService>(); |
服务引用的 URL 组装
- Dubbo 组装服务引用的 URL 的方法是:
ReferenceConfig.createProxy(),核心代码如下图所示:

多注册中心引用服务
- Dubbo 多注册中心引用服务的方法是:
ReferenceConfig.createProxy(),核心代码如下图所示:

通过协议创建 Invoker
- Dubbo 通过协议创建 Invoker 的方法是:
ReferenceConfig.createProxy(),核心代码如下图所示:

提示
- RegistryDirectory 是一个动态服务目录,可感知注册中心配置的变化,它所持有的 Invoker 列表会随着注册中心内容的变化而变化。
- 每当注册中心的内容发生变化后,RegistryDirectory 会动态创建或删除 Invoker,并通过
Router.route()方法进行路由,过滤掉不符合路由规则的 Invoker。
通过 Invoker 构建接口代理实现
- Dubbo 通过 Invoker 构建接口代理实现的方法是:
ReferenceConfig.createProxy(),核心代码如下图所示:

初始化客户端(TCP)
- 当使用的是
dubbo协议时,Dubbo 获取 TCP 客户端的方法是:DubboProtocol.getClients(),核心代码如下图所示:

- 当使用的是
dubbo协议时,Dubbo 初始化 TCP 客户端的方法是:DubboProtocol.initClient(),核心代码如下图所示:

- 当使用的是
dubbo协议时,Dubbo 初始化 TCP 客户端的完整方法调用链是:DubboProtocol.getClients()->DubboProtocol.initClient()->Exchangers.connect()->HeaderExchanger.connect()->Transporters.connect()->NettyTransporter.connect()->NettyClient()->AbstractClient()->AbstractClient.doOpen()->NettyClient.doOpen()
Dubbo 客户端建立 TCP 连接的时机
- Dubbo Consumer 默认采用懒连接机制,Consumer 在引用服务时并不会主动建立与 Provider 的 TCP 连接;只有在 Consumer 第一次真正发起 RPC 调用时,Consumer 才会与 Provider 建立 TCP 连接。
- 除非是显式禁用懒连接机制(如
<dubbo:reference lazy="false">或设置<dubbo:reference init="true">),否则 Dubbo Consumer 默认不会在启动时主动连接 Provider,而是在第一次真正发起 RPC 调用时才建立 TCP 连接。
客户端的通信过程
Dubbo 客户端的通信步骤

Dubbo 客户端的 Invoker 执行
- (1) 从接口代理实现的调用开始(服务引用的入口方法是
ReferenceConfig.get())

- (2) 底层会执行
InvokerInvocationHandler.invoker()->DubboInvoker.doInvoke(),这两个方法的核心源码如下图所示:

Dubbo 请求、响应处理器
ExchangeHandlerAdapter的核心方法如下图所示:

Dubbo 编解码处理
ExchangeCodec、DubboCodec 的核心方法如下图所示:

Dubbo 发送请求数据
- Dubbo 发送请求数据的核心类和方法如下图所示:

Dubbo 服务端接收处理请求


Dubbo 服务端的线程派发
- Dubbo 服务端默认支持以下几种线程派发策略(Dispatcher):
| 参数值 | 含义说明 | 特点与适用场景 |
|---|---|---|
all | 默认值,所有消息均派发到业务线程池 | 最常见配置,网络 I/O 线程只负责读写,业务逻辑全部交由业务线程池处理。 |
direct | 所有消息直接在网络 I/O 线程上执行 | 性能最高但风险大,可能会阻塞网络 I/O 线程,适合极轻量级、无阻塞逻辑的场景。 |
message | 只派发请求、响应消息到业务线程池 | 连接、断开、心跳等消息仍在网络 I/O 线程中处理,适合请求耗时较长的场景。 |
execution | 仅派发请求消息到业务线程池,不包含响应消息 | 响应和其它连接、断开、心跳消息,直接在网络 I/O 线程上执行,适合请求耗时较长的场景。 |
connection | 每个连接独立分配线程池 | 为每个连接创建独立线程池,适合多连接、隔离性要求高的场景。 |
| 可扩展 | 自定义派发策略(SPI 扩展) | Dubbo 通过 SPI 机制支持扩展自定义 Dispatcher 实现,以满足特殊调度需求。 |
提示
Dispatcher 真实的职责是创建具有线程派发能力的 ChannelHandler,比如 AllChannelHandler、MessageOnlyChannelHandler 和 ExecutionChannelHandler 等,其本身并不具备线程派发能力,详细介绍请看 这里。
服务引用总结
Dubbo(2.7.x)服务引用的时序图(源自 Dubbo 官方文档)如下所示:

Dubbo (2.6.6)服务引用的完整调用链如下(仅供参考,并非绝对严谨):
1 | ReferenceConfig.get() ─► 服务引用入口 |
