Dubbo 集群容错与负载均衡详解

Dubbo 集群容错与负载均衡详解

定位:Dubbo 目录第 04 篇(治理能力篇),讲透集群层的容错策略、负载均衡算法、路由规则、降级 Mock 与超时重试------这是面试考察最密集的一篇

适用版本:Dubbo 3.x(JDK 8+/17)


目录


一、集群层定位

1.1 在调用链中的位置

回看 02 篇调用链,集群层是消费方进入网络前的最后一道"逻辑层":

复制代码
代理 → 【集群层(容错 + 路由 + 选址)】 → Filter → 协议层 → 网络

它对上层(代理)隐藏"后面有 N 台机器"的事实,对下层(协议)只发起"对一台机器的单次调用"。

1.2 三个职责、四个抽象

抽象 职责 本篇章节
Cluster 失败后怎么办:重试/快速失败/忽略 第二节
Directory 候选地址从哪来(接注册中心推送,06 篇) ---
Router 候选集先按规则过滤(灰度/泳道) 第四节
LoadBalance 过滤后的候选集里选哪一个 第三节

一次调用的选址流水线:

复制代码
Directory 全量候选
   → Router 链过滤(标签/条件/脚本)
   → LoadBalance 选 1 个
   → 发起调用
   → 失败?交给 Cluster 策略决定下一步

四个抽象全部是 SPI 扩展点(03 篇),都可自定义替换------这是治理能力的扩展性基础。


二、六大容错策略

容错策略通过 cluster 参数配置(默认 failover),全部是 Cluster SPI 的实现:

2.1 Failover(失败自动切换,默认)

复制代码
调用节点 A → 失败 → 换节点 B 重试 → 失败 → 换节点 C
总次数 = 1 + retries(默认 retries=2,共调 3 次)
  • 语义:假设失败是"那台机器的问题",换一台大概率能成;
  • 适用:读操作、幂等写操作;
  • 风险:非幂等场景重试会重复执行;重试会放大下游压力(故障时重试风暴,见 6.4)。

2.2 Failfast(快速失败)

复制代码
调用节点 A → 失败 → 立即抛异常,不重试
  • 适用:非幂等写操作(下单、扣款)、明确只允许执行一次的逻辑;
  • 与重试的关系 :Failfast 下 retries 配置无效------"不重试"正是它的目的。

2.3 Failsafe(失败安全)

复制代码
调用 → 失败 → 吞掉异常,返回空结果,仅记日志
  • 适用:旁路逻辑------写审计日志、埋点上报。这些调用失败不应影响主流程;
  • 注意:它不是"安全地失败",而是"失败了也当没发生",只可用于允许静默丢失的调用。

2.4 Failback(失败自动恢复)

复制代码
调用 → 失败 → 把请求记入内存队列 → 后台定时任务重发
  • 适用:消息通知类、最终一致即可的操作;
  • 注意 :重发队列在内存中,进程重启会丢失;积压过多会占内存,有上限保护(超限丢弃并告警)。

2.5 Forking(并行调用)

复制代码
同时并行调 N 台(forks 参数,如 2)→ 任一台先成功即返回,其余取消
  • 适用:实时性要求极高、可承受资源浪费的场景;
  • 代价:一次调用消耗 N 倍下游资源,是"用钱买延迟"。

2.6 Broadcast(广播调用)

复制代码
逐台调用所有节点 → 任一台失败即整体失败
  • 适用:通知所有节点做本地动作------刷新本地缓存、更新本地配置;
  • 语义:要求"全部成功",一台失败即失败(不做部分成功的语义,需自定义)。

2.7 选型速查

场景 策略 原因
查询接口 Failover 幂等,重试安全
下单/扣款 Failfast + retries=0 非幂等,重复执行有资损风险
审计日志 Failsafe 失败可静默
消息通知 Failback 最终一致即可
毫秒级实时 Forking 延迟优先
缓存刷新通知 Broadcast 需要全部节点执行

自定义策略 :实现 Cluster 接口(SPI),如"先试主集群、失败切备集群"的多机房容错。


三、负载均衡算法

loadbalance 参数,默认 random,全部是 LoadBalance SPI 实现。

3.1 Random(加权随机,默认)

按权重随机选择。权重来自注册信息(weight 参数),默认 100。

复制代码
节点 A weight=100, B weight=300
→ 随机落点中 3/4 概率选中 B

特点:单次有随机性,大样本下调用量趋近权重比例;实现简单、无状态。

3.2 RoundRobin(加权轮询)

按权重比例轮询分配。权重 100:300 时,每 4 次调用中 1 次给 A、3 次给 B。

复制代码
实现细节(平滑加权轮询):
每个节点维护 currentWeight,每轮所有节点 currentWeight += weight,
选 currentWeight 最大者,选中后 currentWeight -= totalWeight
→ 分配序列均匀打散(不会连续 3 次都打 B),避免瞬时集中

特点:分配精确、序列平滑;对慢节点无感知------慢节点照样分到同样多的请求。

3.3 LeastActive(最少活跃数)

"活跃数"= 已发起、未返回的调用数量。每次选活跃数最小的节点。

复制代码
直觉:节点处理得越快,手里的在途请求越少 → 活跃数小
结果:慢节点自然分到更少流量(自适应)

特点:对性能差异自适应,无需人工调权重;活跃数相同则退化为加权随机。注意统计是消费方本地视角,多消费方各自统计。

3.4 ConsistentHash(一致性哈希)

相同调用参数路由到同一节点:

复制代码
① 把每个节点映射到哈希环上,并按权重生成虚拟节点(默认 160 个/实例)
② 对调用参数(默认取第一个参数,可配 hash.arguments)做哈希
③ 顺时针找第一个虚拟节点 → 定位到物理节点

关键性质:

  • 会话粘滞:同一用户/参数的请求稳定落在同一节点,利于该节点做本地缓存;
  • 增删节点影响小:只影响环上相邻的一小段,不会全局洗牌(虚拟节点让分布更均匀);
  • 虚拟节点的作用:物理节点少时直接映射分布不均,虚拟节点放大采样点使负载均衡。

3.5 ShortestResponse(最短响应,3.x 新增)

估算每个节点的"平均响应时间"(滑动窗口内的平均耗时),选估算最快的,同分加权随机。思想与 gRPC 的 P2C(Power of Two Choices)相近:用响应速度直接做选址依据。

与 LeastActive 的区别:LeastActive 看"在途数量"(间接指标),ShortestResponse 直接看历史耗时(直接指标),对"慢但并发低"的节点识别更准。

3.6 算法对比速查

算法 状态 对慢节点感知 典型场景
Random 无 无 通用默认
RoundRobin 每节点游标 无 机器同质、求精确分配
LeastActive 活跃计数 有 机器异构/性能不均
ConsistentHash 哈希环 弱 参数级粘滞、本地缓存
ShortestResponse 耗时窗口 强 追求低尾延迟

四、路由规则

路由在负载均衡之前 执行,职责是把候选集按规则缩小。规则通常由配置中心动态下发,运行时生效、无需重启。

4.1 条件路由(Condition)

基于 URL 参数的 if-then 表达式:

复制代码
# 含义:来源是 10.20.153.10 的请求,只允许调往 10.20.153.11
host = 10.20.153.10 => host = 10.20.153.11

# 常见用途:白名单测试机
=> host != 172.22.3.91        # 除测试机外都可见(生产流量绕开测试机)

典型用途:测试机隔离、机房就近路由的粗粒度版本、应急摘除某些节点(=> 空 即全拒)。

4.2 标签路由(Tag)

给实例打标签(如 tag = gray),请求携带标签则只路由到同标签实例:

复制代码
提供方:dubbo.provider.tag = gray        (灰度机器组)
消费方:请求上下文设置 attachment tag = gray
→ 灰度流量只落在灰度机器;无标签流量不落到灰度机器(隔离)

这是全链路灰度/泳道的基础设施:发布时给新版本机器打标签,验证流量定向打过去,出问题只影响标签内。

4.3 脚本路由(Script)

用 Groovy/JavaScript 等脚本写任意过滤逻辑:

groovy 复制代码
// 只保留非 10.x 段的提供者(示意)
invokers.findAll { !it.url.host.startsWith("10.") }

表达力最强,但脚本执行有开销且需谨慎审核内容(配置中心下发的脚本等同生产代码)。

4.4 三种路由的取舍

类型 表达力 维护成本 适用
条件路由 低 低 简单白名单/黑名单/摘机
标签路由 中 低 灰度、泳道(推荐)
脚本路由 高 高 复杂规则,少用

五、服务降级与 Mock

5.1 Mock 的两种模式

java 复制代码
// 失败才降级(自动):正常调用,异常时返回 Mock 结果
@DubboReference(mock = "fail:return null")

// 强制降级(手动):不调远端,直接返回 Mock 结果(应急开关)
@DubboReference(mock = "force:return empty")
模式 行为 用途
fail: 调用失败后返回 Mock 自动兜底:弱依赖挂了不拖垮主流程
force: 根本不发起调用 手动熔断开关:故障期人为切断某依赖

5.2 自定义 Mock 类

复杂返回(非字面量)用实现同接口的 Mock 类:

java 复制代码
public class GreetingServiceMock implements GreetingService {
    @Override
    public String sayHello(String name) {
        return "服务暂不可用";    // 降级返回
    }
}
java 复制代码
@DubboReference(mock = "com.demo.GreetingServiceMock")
private GreetingService greetingService;

Mock 类必须有无参构造;它的实例在消费方本地创建执行,不经过网络。

5.3 降级与容错的关系

复制代码
容错(Cluster):在"服务还活着"的前提下处理单次失败
降级(Mock):   在"服务不值得调/调不通"时给出替代结果
两者叠加:fail: mock 本质是容错链兜底的最后一环

六、超时与重试机制

6.1 超时配置在谁身上生效

一个高频误解:超时不是"提供方执行超过就中断",而是消费方的等待上限。

复制代码
消费方发起调用,启动计时(默认 1000ms)
   → 期间提供方还在执行,但消费方到点即抛 TimeoutException
   → 提供方是否继续执行?默认继续跑完(除非业务自行检查)

推论:

  1. 超时只节省消费方线程,不自动节省提供方资源;
  2. 超时 + 重试组合在非幂等场景 = 重复执行风险(6.4)。

6.2 配置优先级链

复制代码
消费方方法级 > 消费方接口级 > 消费方全局
   > 提供方方法级 > 提供方接口级 > 提供方全局 > 默认 1000ms

规则两条:

  • 消费方优先:等多久由等待方说了算;
  • 小范围优先:方法级覆盖接口级,接口级覆盖全局。

提供方配置超时的意义:作为"服务方自知的合理耗时建议",在消费方没配时兜底。

6.3 重试的计数语义

java 复制代码
@DubboService(retries = 2)   // 首次 + 2 次重试 = 最多调 3 次

注意三个细节:

  1. retries 是额外重试次数,不是总次数;
  2. 重试默认换节点(配合 Failover),不是打同一台;
  3. 只对 RpcException 中的超时/网络类失败重试,业务异常不重试(否则错误被放大)。

6.4 重试风暴与防护

故障场景下的恶性循环:

复制代码
提供方变慢 → 消费方超时 → Failover 重试 → 提供方请求量 ×3
→ 更慢 → 更多超时 → 更多重试 → 雪崩

防护手段:

  • 非幂等/写多场景调低 retries = 0;
  • 提供方限流(08 篇讲线程池/并发控制);
  • 超时值合理:显著大于 P99 耗时但不无限大;
  • 配合路由摘除故障节点(比无脑重试更优)。

七、总结

  1. 集群层三职责:容错(失败怎么办)、路由(候选集过滤)、选址(选哪台),对应 Cluster / Router / LoadBalance 三个 SPI,Directory 提供候选。
  2. 六大容错:Failover 默认(读/幂等)、Failfast(非幂等写)、Failsafe(旁路)、Failback(通知类,内存队列重启丢失)、Forking(实时,资源换延迟)、Broadcast(全节点执行)。
  3. 负载均衡:Random 加权随机(默认)、RoundRobin 平滑加权、LeastActive 自适应慢节点、ConsistentHash 参数粘滞 + 虚拟节点、ShortestResponse 按历史耗时选址。
  4. 路由:条件(白黑名单/摘机)、标签(灰度泳道,推荐)、脚本(高表达力,慎用);规则经配置中心动态下发。
  5. 降级 :fail: 失败后兜底、force: 手动切断;自定义 Mock 类处理复杂返回值。
  6. 超时重试纪律 :超时是消费方等待上限;消费方配置优先;非幂等写必须 retries=0 + Failfast;警惕重试风暴放大故障。

八、常见高频面试题

1. Dubbo 有哪些集群容错策略?默认是哪个?分别适用什么场景?

要点:六种,默认 Failover。Failover 失败换节点重试(读/幂等写);Failfast 一次失败即抛异常(非幂等写,如下单扣款);Failsafe 失败吞掉记日志(审计埋点等旁路);Failback 失败入内存队列后台重发(通知类,注意重启丢失);Forking 并行调多台一个成功即返回(实时性优先,资源换延迟);Broadcast 广播所有节点任一失败即失败(缓存刷新通知)。

2. Dubbo 有哪些负载均衡算法?各自原理与适用?

要点:默认 Random 加权随机,大样本趋近权重比;RoundRobin 平滑加权轮询,分配精确但对慢节点无感知;LeastActive 选在途活跃数最少的节点,慢节点自动少接活;ConsistentHash 一致性哈希 + 虚拟节点,相同参数落同一节点,用于粘滞与本地缓存;ShortestResponse(3.x)按滑动窗口平均耗时选最快节点。异构机器选 LeastActive/ShortestResponse,同质选 RoundRobin,参数粘滞选一致性哈希。

3. 一致性哈希在 Dubbo 里怎么实现?虚拟节点解决什么问题?

要点:把每个实例按权重映射到哈希环并生成虚拟节点(默认 160 个/实例),对调用参数(默认第一个参数,可配)哈希后顺时针找最近虚拟节点定位物理节点。虚拟节点解决物理节点少时分布不均的问题------放大采样点让流量均匀;同时保证增删节点只影响相邻区段,不全局洗牌。适用于同参数请求需要粘滞到同一节点的场景。

4. retries 配置的含义是什么?非幂等接口该怎么配?

要点:retries 是额外重试次数(总调用 = 1 + retries),默认 2;配合 Failover 时每次重试换节点;只对超时/网络类异常重试,业务异常不重试。非幂等接口(下单、扣款)必须设 retries=0 且 cluster=Failfast,否则超时重试会重复执行造成资损;同时注意重试风暴会在故障时放大下游压力。

5. Dubbo 超时是作用在哪一端?提供方超时会中断执行吗?

要点:超时是消费方的等待上限------到点消费方抛 TimeoutException 不再等,但提供方默认继续执行完(不会被打断)。所以超时只保护消费方线程,不自动释放提供方资源。配置优先级:方法级 > 接口级 > 全局,且消费方配置覆盖提供方;提供方配置作为"合理耗时建议"在消费方未配时兜底。

6. 什么是重试风暴?如何防护?

要点:提供方变慢 → 消费方超时 → Failover 重试 → 提供方请求量翻倍 → 更慢,形成雪崩放大回路。防护:写接口/非幂等调低重试次数;超时设置为显著大于 P99 但有限;提供方做并发控制与限流;更优的是用路由/故障摘除把流量从坏节点移开,而不是无差别重试;必要时用熔断(失败率达到阈值暂停调用)替代重试。

7. 灰度发布用 Dubbo 怎么实现?

要点:核心是标签路由。给新版本实例配置 tag=gray 标签,验证请求在上下文携带同标签,则只路由到灰度实例;未带标签的流量不会落到灰度实例,天然隔离。标签规则可由配置中心动态下发与回收。也可用条件路由做基于来源的粗粒度分流。灰度验证通过后摘标签全量,出问题秒级切回(改配置不改代码)。

8. mock = "fail:return null" 和 "force:return true" 的区别?

要点:fail: 是自动降级------先正常发起远程调用,只有失败(异常/超时)时才返回 Mock 结果,用于弱依赖兜底;force: 是强制降级------根本不发起远程调用直接返回,相当于人工熔断开关,用于故障期快速切断某依赖恢复主链路。两者都支持自定义 Mock 类(实现同接口)以返回复杂对象,Mock 在消费方本地执行不经过网络。

9. LeastActive 和 ShortestResponse 都能感知慢节点,有什么区别?

要点:LeastActive 用"在途请求数"作间接指标------处理快的节点手里积压少,活跃数小,适合并发视角;但它对"慢但当前并发低"的节点感知滞后。ShortestResponse 直接统计滑动窗口内平均响应耗时选最快节点(类似 P2C 思想),对慢节点识别更直接准确,是 3.x 新增。机器异构、性能不均时两者都优于静态权重。

10. 条件路由能做什么?应急摘除一台故障机器怎么操作?

要点:条件路由是 if-then 表达式,基于 URL 参数(host、端口、参数等)过滤候选集。用途:白名单测试机(生产流量 => 排除测试机)、来源定向路由、应急摘机。应急摘除故障机器:下发规则把该机器从候选中排除(如配置"=> host != 故障IP"或直接令目标集不含它),动态生效无需重启,比等待健康探测摘除更快;机器恢复后再移除规则。

相关推荐
Dawson Zhu1 小时前
Agent系统工程质量评估体系:原理解析与工程实践
人工智能·语言模型·架构·aigc·agi
Java的搬运工1 小时前
2026 AI Agent 实战:五层架构、MCP 工具调用与十条避坑清单 合集 - AI行业观察
人工智能·架构·智能体·大模型应用·langgraph·aiagent·mcp
天远API2 小时前
零信任架构实战:基于天远全网运营商三要素构建自动化骑手准入网关
运维·人工智能·架构·自动化
nvd112 小时前
Flink 极简入门与零常驻批处理架构实战:从双模式辨析到 GitOps 通用模具治理
大数据·架构·flink
mldong2 小时前
同一套充血模型,八种语言八种长相
后端·架构
GreenTea10 小时前
我把 Agent 的 while 循环拆掉了:一种你可能没想到的 Agent 架构
前端·后端·架构
m0_5873830011 小时前
广州24小时自助健身房解决方案实战指南与系统部署要点
java·spring·小程序·架构·需求分析
狂奔蜗牛(bradley)13 小时前
拒绝SOEM黑盒!死磕EtherCAT主站:ARM+FPGA架构、DC同步与状态机修复等,50+篇实战开发文档全公开
arm开发·fpga开发·架构
辛迪聊物业数字化13 小时前
智慧社区SaaS平台架构拆解:从物业收费到IoT联动的落地实现
物联网·架构