上一篇 《Dubbo 负载均衡:四种策略与源码分析》 讲了流量怎么分。这一篇讲失败怎么办——服务调用失败时,Dubbo 怎么容错、怎么重试,以及集群容错的整体架构。
集群容错架构图
先看整张调用链架构图:

一次调用从发起到执行,完整经过五个环节:
外部请求 invoke
↓
Invoker(集群入口,持有 Cluster)
↓ merge(集群合并,按容错模式包装)
Cluster(集群容错,5 种模式:Failover/Failfast/Failsafe/Failback/Forking)
↓ list
Directory(服务目录,从注册中心拿到 List<Invoker>:Registry/Static)
↓ List<Invoker>
Router(路由规则,过滤掉不能执行的 Invoker:Script/Condition)
↓ List<Invoker>
LoadBalance(负载均衡,选出一个 Invoker:Random/RoundRobin/LeastActive)
↓ Invoker
最终执行远程调用
调用链的职责分工:
| 环节 | 职责 | 内置实现 |
|---|---|---|
| Cluster | 集群容错入口,决定失败后怎么处理 | Failover、Failfast、Failsafe、Failback、Forking |
| Directory | 服务目录,提供本次集群的全部 Invoker | Registry(从注册中心)、Static(直连静态) |
| Router | 路由规则,从全部 Invoker 中筛掉不可用的 | Script、Condition |
| LoadBalance | 负载均衡,从可用 Invoker 中选一个执行 | Random、RoundRobin、LeastActive |
一次调用的四个步骤
笔记里原话拆解:
- 在 Directory 中找出本次集群中的全部 invokers。
- 在 Router 中,将上一步的全部 invokers 挑选出能正常执行的 invokers。
- 在 LoadBalance 中,将上一步能正常执行的 invokers 中,根据配置的负载均衡策略,挑选出需要执行的 invoker。
- 执行 invokers 时,例如在默认的 FailoverClusterInvoker 中执行,根据配置的重试次数失败重试。
全部 Invoker(Directory 从注册中心获取)
↓ Router 过滤
可用的 Invoker(剔除路由规则排除的)
↓ LoadBalance 选择
选中的一个 Invoker
↓ Cluster 容错执行(如 Failover 失败重试)
调用完成
五种容错模式
cluster 属性决定失败时的行为:
| 模式 | 类名 | 失败后行为 | 适用场景 |
|---|---|---|---|
| Failover | FailoverClusterInvoker | 失败自动切换,重试其他节点 | 默认;读操作 |
| Failfast | FailfastClusterInvoker | 失败立即报错,不重试 | 非幂等写操作 |
| Failsafe | FailsafeClusterInvoker | 失败吞掉异常,只记日志 | 无关紧要的旁路调用 |
| Failback | FailbackClusterInvoker | 失败自动记录,定时重发 | 消息通知、异步任务 |
| Forking | ForkingClusterInvoker | 并行调用多个节点,任一成功即返回 | 高实时性读操作 |
<!-- 默认 Failover:重试 2 次 -->
<dubbo:reference id="userService" interface="com.example.UserService"
cluster="failover" retries="2"/>
Failover:失败自动切换(默认)
失败后自动切换到其他节点重试,retries 控制重试次数:
调用 A 失败 → 重试 B → 重试 C ...(最多 retries 次)
- 适用:读操作、幂等操作(查数据、无副作用的计算)。
- 注意:重试会增加总耗时(每次重试都是一次完整调用);非幂等操作严禁开启重试。
Failfast:快速失败
只调用一次,失败立即抛出异常,不重试:
调用 A 失败 → 立即抛出异常
- 适用:非幂等写操作(下单、扣款、转账)——防止重试造成重复扣款。
- 代价:一次网络抖动就失败,没有容错余地。
Failsafe:失败安全
失败后吞掉异常,只记录日志,不抛给调用方:
调用 A 失败 → 记录日志 → 正常返回(假装成功)
- 适用:无关紧要的旁路操作(打日志、上报指标、清理缓存),失败不影响主流程。
- 注意:错误被吞掉,需要靠日志监控发现问题。
Failback:失败自动恢复
失败后记录失败请求,定时重发(异步补偿,不再同步等待):
调用 A 失败 → 记录到失败队列 → 后台定时重试
- 适用:消息通知、异步任务等允许延迟、最终一致性的场景。
- 注意:重发是异步的,调用方不会感知失败,业务上要有兜底。
Forking:并行调用
并行调用多个节点(forks 指定并行数),任一成功即返回:
同时调用 A、B(forks=2)
A 成功 → 立即返回,B 的结果丢弃
A、B 都失败 → 抛异常
- 适用:对实时性要求高的读操作(并行发请求,取最快成功的结果)。
- 注意:会成倍消耗 Provider 资源,
forks不能太大。
容错模式选型总结
| 业务场景 | 推荐模式 | 原因 |
|---|---|---|
| 读操作、默认 | Failover | 失败重试,提升成功率 |
| 下单、扣款等写操作 | Failfast | 防重复扣款,不重试 |
| 日志、上报等旁路 | Failsafe | 失败不影响主流程 |
| 消息通知、异步 | Failback | 定时重发,最终一致 |
| 高实时读操作 | Forking | 并行调用,最快成功 |
服务暴露:URL → Invoker → Exporter
集群容错讲的是消费侧,最后补一块生产侧的核心原理——服务暴露。
笔记里的原话一句话概括:
服务暴露:URL 转为 Invoker,Invoker 转为 Exporter。
展开看:
服务暴露(Provider 侧启动流程)
====================
1. 解析配置 → 生成服务对应的 URL
(dubbo://10.129.83.213:20888/com.example.UserService?...)
2. URL 转为 Invoker
(把 URL 描述的远程服务包装成可调用对象,持有协议、地址、序列化方式)
3. Invoker 转为 Exporter
(Exporter 把 Invoker 暴露出去:启动 Netty 监听端口 + 向注册中心注册)
对应到架构图里,Consumer 侧的 Invoker 是”远程调用的抽象”,Provider 侧的 Invoker 是”本地实现的包装”——两边都是 Invoker,一侧负责发、一侧负责收,这也是 Dubbo 透明化调用的核心设计。
服务引用的镜像流程
消费侧的”服务引用”是暴露的逆过程:
1. Consumer 从注册中心拿到服务 URL
2. URL 转为 Invoker(代理对象,真正调用时发出 RPC 请求)
3. Invoker 转为代理类(用户拿到的是接口代理,调方法即远程调用)
所以用户代码里 @Reference 注入的 UserService,本质是 Dubbo 生成的动态代理,调用方法时由代理把参数序列化、发到 Provider、反序列化结果返回——这就是”透明化”的真相。
小结
- 调用链:Cluster(容错)→ Directory(目录)→ Router(路由)→ LoadBalance(选择)→ Invoker(执行)。
- 五种容错:Failover(重试,默认)、Failfast(快速失败)、Failsafe(吞异常)、Failback(异步重发)、Forking(并行调用)。
- 选型:读用 Failover、写用 Failfast、旁路用 Failsafe、异步用 Failback、实时读用 Forking。
- 服务暴露:URL → Invoker → Exporter;服务引用是逆过程:URL → Invoker → 动态代理。
至此 Dubbo 系列五篇完结:入门 → 服务拆分 → 注册中心与直连 → 负载均衡 → 集群容错。欢迎收藏,后续有新的 Dubbo 实践再补充。