熔断详解
熔断(Circuit Breaker) 是高可用系统应对「下游故障」的核心手段:当被调用的服务出现持续错误或超时时,熔断器主动「跳闸」,在一段时间内直接快速失败、不再发起真实调用,从而避免上游线程被拖死、引发级联雪崩。
限流是「我主动控制流量」,熔断是「下游病了我不再打扰它」——建议和「限流详解」对照着看,两者正好凑成稳定性三板斧的两条腿。下面就从故障怎么传染说起,一路讲透三态状态机、触发条件、恢复策略、主流框架,以及和限流/降级/重试怎么打配合。
本文脉络: |
一、为什么需要熔断
1.1 故障是如何传播的
分布式系统中,一次请求往往要经过多个下游服务。任何一个下游变慢或故障,都会沿调用链向上传染。
调用链: 用户中心 → 订单服务 → 支付服务 → 银行网关 |
核心问题:下游一旦变慢(而不是立刻报错),上游会「执着地等待」,把宝贵的线程/连接资源耗尽在自己这一侧——这比下游直接挂掉更危险。
1.2 单纯的超时和重试为什么不够
| 手段 | 作用 | 局限 |
|---|---|---|
| 超时 | 避免无限等待 | 超时时间内仍占用线程;故障持续时每次都等到超时才释放 |
| 重试 | 应对瞬时抖动 | 下游持续故障时,重试反而放大流量,加速雪崩 |
| 线程池隔离 | 限制单一下游占用的资源 | 池子打满后,该下游的正常请求也受影响 |
超时和重试是「每次调用独立判断」,没有记忆。当下游已经持续故障时,继续调用它毫无意义——明明知道会失败,却每次都要等一个超时才放弃。
熔断的本质:给调用加上「记忆」——下游连续失败到一定程度,就认定它「病了」,在一段时间内直接快速失败,不再发起真实调用,给下游恢复的时间,也保护自己不被拖死。
二、熔断器状态机(Closed / Open / Half-Open)
这是熔断器的核心,类比电气保险丝:电流过大时保险丝熔断(Open),保护电路;修复后人工复位(Closed)。
2.1 三态模型
失败率达阈值 |
2.2 三个状态详解
| 状态 | 行为 | 转入条件 |
|---|---|---|
| Closed(关闭) | 熔断器不工作,所有请求正常放行,统计失败率 | 初始状态 / Half-Open 探测成功 |
| Open(打开) | 所有请求直接快速失败(走降级/Fallback),不发起真实调用 | Closed 状态下失败率超阈值 |
| Half-Open(半开) | 放行少量请求做试探,其余快速失败 | Open 状态持续一段时间(sleep window)后自动转入 |
2.3 Open 状态:全拒,不是按比例拒
限流和熔断最容易混的地方就在这——限流是「按比例放行」,熔断 Open 是「全部拒绝」。先给结论:Open 状态下,100% 的请求都直接快速失败,没有「按比例拒绝」这一说。
Open 期间(如 30s 冷却): |
为什么必须是全拒:Open 的目的就是「认定下游病了,这段时间完全不打扰它」,给下游完整的恢复时间。如果还放一部分请求过去,下游就始终被持续打扰、没机会恢复,熔断的意义就没了。
「按比例放行」不是 Open 的事,而是 Half-Open 的事——而且即便在 Half-Open,主流框架通常也不是「按百分比」,而是一个固定的试探数量:
| 框架 | Half-Open 放行策略 |
|---|---|
| Hystrix | 默认只放行 1 个请求试探 |
| Resilience4j | 放行 10 个(permittedNumberOfCallsInHalfOpenState,可配) |
| Sentinel | 熔断时间窗结束后进入半开探测 |
| 部分高级实现 | 渐进式恢复:按 10% → 30% → 60% → 100% 逐步放行 |
换句话说:真正的「百分比拒绝」是限流的特征,不是熔断 Open 的特征。限流和熔断的完整对比放在第七章,这里先记住这点就行。
2.4 一个完整周期
1. Closed 状态:正常处理 1000 个请求,统计窗口内错误率 |
为什么需要 Half-Open:直接从 Open 切回 Closed 风险太大——一旦流量瞬间涌入,下游可能还没真正恢复又被压垮。Half-Open 用「少量试探」谨慎验证,是「灰度恢复」思想在熔断里的体现。
三、触发条件
熔断打开的依据是「下游健康度」,主流框架的判定维度:
| 维度 | 含义 | 典型阈值 |
|---|---|---|
| 错误率 | 统计窗口内失败请求占比 | 错误率 ≥ 50% |
| 慢调用率 | 统计窗口内超时请求占比 | 慢调用率 ≥ 60% |
| 失败次数 | 统计窗口内绝对失败数 | 1 分钟失败 ≥ 100 次 |
3.1 错误率(最常用)
统计窗口:最近 10 秒 |
为什么要有「最小请求数」:如果窗口里只有 2 个请求且都失败了,错误率 100% 看起来很严重,但其实只是样本不足。设最小请求数(如 20)保证统计才有意义。
3.2 慢调用率(应对「假死」)
有些故障不是直接报错,而是响应变慢。错误率熔断捕捉不到「全部超时但都返回 200」的假死场景,于是引入慢调用率:
慢调用定义:RT > maxRT(如 500ms) |
适用:对延迟敏感、且容易「慢但不错」的下游(如依赖大模型、搜索引擎的接口)。
四、恢复策略
熔断打开后,何时、如何回到正常状态,决定了系统的恢复体验。
4.1 sleep window(冷却时间)
Open 持续的时间,给下游喘息恢复。典型 5s~60s。
太短:下游还没恢复就试探,再次失败 → 反复 Open/Half-Open 抖动 |
4.2 Half-Open 探测策略
策略一:固定请求数试探 |
选型:简单场景用固定请求数(Hystrix 默认);对恢复平滑性要求高用渐进放行(部分新版框架支持)。
五、常见熔断框架
5.1 Hystrix(Netflix,经典但已停止维护)
// 继承 HystrixCommand,声明熔断 + 降级 |
特点:经典实现,基于线程池/信号量隔离 + 熔断 + 降级。2018 年起进入维护模式,不建议新项目使用,但其设计思想是后续框架的基础。
5.2 Resilience4j(Hystrix 的现代继任者)
CircuitBreakerConfig config = CircuitBreakerConfig.custom() |
特点:
- 基于 Java 8 函数式编程,轻量无依赖
- 支持错误率 + 慢调用率双重判定
- 与 Spring Boot / Reactor 深度集成
- 新项目主流选择
5.3 Sentinel(阿里,国内主流)
// 通过资源名 + 规则配置熔断 |
特点:
- 熔断 + 限流 + 系统自适应保护一体
- 支持错误率、慢调用率、异常数三种策略
- 带可视化 Dashboard,可动态调整规则、实时查看熔断状态
- 国内 Java 生态最主流
六、熔断后的兜底(降级 / Fallback)
熔断打开后请求会快速失败,但对用户的最终表现取决于降级逻辑。没有降级的熔断等于「直接报错」,体验很差。
6.1 常见降级手段
| 场景 | 降级方式 |
|---|---|
| 读接口(商品详情) | 返回缓存/本地默认值/热门推荐 |
| 列表查询 | 返回首屏固定数据,隐藏非核心模块 |
| 写接口(下单) | 不能降级为假成功!改为异步化(写 MQ 后提示「处理中」) |
| 非核心功能(推荐、评论) | 直接隐藏模块,保证主流程 |
|
6.2 降级的关键原则
① 降级要快:不能再调用会失败的服务(否则降级没意义) |
七、与限流、降级、重试的协同(稳定性三板斧)
这一节与「限流详解」第八章呼应,建议对照阅读。
7.1 四者的职责分工
┌──────────────────────────────────────────────────────┐ |
7.2 熔断与重试的冲突(重点)
危险组合:上游重试 + 下游故障 |
7.3 限流 vs 熔断(最易混淆)
| 维度 | 限流 | 熔断 |
|---|---|---|
| 主动/被动 | 主动(我决定放多少进来) | 被动(下游状态决定调不调) |
| 保护对象 | 保护自己/下游的容量 | 保护下游不再被请求压垮 |
| 触发依据 | 流量/QPS | 错误率/超时率/失败次数 |
| 触发后 | 部分请求拒绝 | 全部请求快速失败(一段时间) |
| 状态 | 无状态(每个请求独立判断) | 有状态(开/半开/关) |
一句话区分:限流是「我主动控制流量」,熔断是「下游病了我不再打扰它」。
八、生产实践与常见陷阱
8.1 最佳实践
① 熔断要配在「依赖外部/不可控」的调用上: |
8.2 常见陷阱
| 陷阱 | 后果 | 对策 |
|---|---|---|
| 阈值过低 | 正常抖动就熔断,频繁误断 | 设最小请求数 + 合理阈值,参考历史 P99 |
| 冷却时间太短 | 反复 Open/Half-Open 抖动 | 冷却时间 > 下游平均恢复时间 |
| 降级调用同一故障源 | 降级也失败,等于没降级 | 降级走缓存/本地数据,不再调故障下游 |
| 熔断粒度过粗 | 一个下游故障影响所有调用方 | 按接口/资源粒度分别熔断(资源隔离) |
| 只熔断不告警 | 故障被「掩盖」,无人知晓 | 熔断状态变化必须触发告警 |
九、面试速答
| 问题 | 速答要点 |
|---|---|
| 熔断是什么?解决什么问题? | 下游持续故障时主动「跳闸」快速失败,避免上游线程被拖死、级联雪崩。解决「故障传播」。 |
| 熔断器有几个状态? | 三态:Closed(正常放行)/ Open(全部快速失败)/ Half-Open(少量试探恢复)。 |
| 为什么需要 Half-Open? | 直接切回 Closed 风险大,Half-Open 用少量请求试探,是「灰度恢复」,避免流量瞬间涌入再次压垮下游。 |
| 熔断的触发条件? | 错误率、慢调用率、失败次数。最常用错误率,慢调用率用于捕捉「慢但不错」的假死。 |
| 熔断和限流的区别? | 限流主动控制进来的流量(看 QPS);熔断被动响应下游故障(看错误率),熔断后全部快速失败。 |
| 熔断和重试的关系? | 熔断打开时应抑制重试。只有重试没有熔断,故障时重试会放大流量、加速雪崩。 |
| 常见的熔断框架? | Hystrix(经典、已停维护)、Resilience4j(Hystrix 继任者、新项目主流)、Sentinel(阿里、国内主流、带 Dashboard)。 |
| 熔断打开后怎么办? | 走降级(Fallback):读接口返回缓存/默认值,写接口转异步,对用户返回可接受的数据而非报错。 |