Skip to content

熔断器原理详解:三态、错误率与 fallback

基于 Opossum 8.x · 核于 2026-08

速查

  • 状态机三态:Closed(闭合,正常放行+统计)、Open(打开,全拒+等 resetTimeout)、Half-Open(半开,放一个试探)。转换:Closed→Open(错误率超阈值)、Open→Half-Open(resetTimeout 到期)、Half-Open→Closed(试探成功)/Half-Open→Open(试探失败)。
  • Closed 状态职责:放行所有请求 + 在滑动窗口内统计错误率 + 检查是否该跳闸。一旦错误率 > errorThresholdPercentage(默认 50%)且调用量 ≥ volumeThreshold(默认 0),触发 open 事件,切到 Open。
  • Open 状态职责所有请求直接 reject(触发 reject 事件)或走 fallback,根本不调下游。这是「快速失败」——毫秒级返回,不占上游资源。持续 resetTimeout(默认 30s)后切到 Half-Open。
  • Half-Open 状态职责:放一个请求试探下游恢复情况。成功 → 触发 close 事件回 Closed;失败 → 触发 open 事件回 Open,重新等 resetTimeout。Half-Open 是「自动恢复」的关键——既试探又不让流量洪流冲击刚恢复的下游。
  • 错误率公式错误率 = 窗口内失败次数 / 窗口内总调用次数。失败包括:业务函数 reject/throw、超时(timeout 触发)、被熔断 reject(级联)。在 rollingCountTimeout(默认 10s)的滑动窗口内、按 rollingCountBuckets(默认 10 桶)分桶统计。
  • volumeThreshold 的作用:最小调用量门槛。窗口内调用量 < volumeThreshold 时不跳闸(即使错误率 100%)——避免「调了 1 次失败就误跳闸」。生产建议设非零值(如 5)。
  • fallback 触发条件:①熔断器 Open/Half-Open 时请求被 reject;②调用失败(业务异常);③调用超时。fallback 接收与 fire 相同的参数,返回兜底结果。fallback 被触发仍计为失败(计入错误率),直到 Closed 恢复主路径。
  • 事件全清单fire(发起调用)、success/failure/timeout(调用结果)、reject(被熔断拒绝)、open/close/halfOpen(状态转换)、fallback(降级触发)、semaphoreLocked(并发达容量上限)、healthCheckFailed(自定义健康检查失败)、shutdown(关闭)。
  • 容量(capacity)与并发控制:Opossum 可设 maxCapacity 限制最大并发调用数,超出的触发 semaphoreLocked 拒绝——这是熔断器内置的「信号量」限流,防止并发洪流。
  • 健康检查(healthCheckFunc):可配置自定义健康检查函数,Half-Open 时除了试真实调用,也可先跑健康检查判断下游是否活着。

一、Closed 状态:正常放行与统计

Closed 是熔断器的「默认/恢复」状态。在这个状态下:

javascript
// Closed 状态的每次 fire:
breaker.fire(args)
  → 检查是否超容量(capacity)     ← 超了触发 semaphoreLocked
  → 检查状态                       ← Closed,放行
  → 执行业务函数(带 timeout)
  → 成功:记 success,滑入窗口
  → 失败/超时:记 failure/timeout,滑入窗口
  → 检查窗口:错误率 > 阈值 且 调用量 ≥ volumeThreshold?
     → 是:触发 open 事件,切到 Open
     → 否:继续 Closed
  • 滑动窗口统计:每次调用结果(成功/失败/超时)都记入当前时间桶。rollingCountTimeout(默认 10s)决定窗口长度,rollingCountBuckets(默认 10)决定桶数(每秒一桶)。窗口随时间滑动,老桶滑出不算。
  • volumeThreshold 的意义:避免低调用量下的误判。如果窗口内只调了 1 次且失败了,错误率 100%,但样本太小——设 volumeThreshold=5 表示「至少调 5 次才统计」,5 次以下即使全失败也不跳闸。
  • 超时也算失败:单次调用超过 timeout(默认 10s,生产建议调小到秒级)触发 timeout 事件,同时计为失败入窗口。

二、Open 状态:快速失败与资源释放

一旦跳到 Open,熔断器的行为彻底改变——根本不调下游

javascript
// Open 状态的每次 fire:
breaker.fire(args)
  → 检查状态                       ← Open
  → 不执行业务函数!直接:
     → 有 fallback:执行 fallback,触发 fallback 事件
     → 无 fallback:reject Promise,触发 reject 事件
  → 毫秒级返回,不占上游线程/连接
  • 为什么这是核心保护:Open 状态下,原本要等 30s 超时的请求现在毫秒级失败——上游的线程池、连接池瞬间释放,上游自己活下来,能继续服务其他请求。这就是「阻断雪崩」。
  • resetTimeout 计时:进入 Open 时开始计时,resetTimeout(默认 30s)到期后切到 Half-Open 试探。这个周期要权衡:太短(频繁试探冲击刚恢复的下游)、太长(下游恢复了也不让用)。
  • fallback 仍计失败:即使走了 fallback 返回了兜底数据,熔断器仍把这次记为失败(因为主路径没成功)——错误率继续累积,直到 Half-Open 试探成功切回 Closed。

三、Half-Open 状态:试探恢复

resetTimeout 到期后,熔断器进入 Half-Open——只放一个请求试探:

Open(已等满 resetTimeout)
  → 切到 Half-Open
  → 下一个 fire 请求:实际调用下游(试探)
     → 成功:触发 close 事件 → 回 Closed(恢复正常)
     → 失败:触发 open 事件 → 回 Open(再等一个 resetTimeout)
  → 试探期间的其它请求:仍 reject/fallback
  • 为什么只放一个:下游可能刚恢复但还很脆弱,如果 Half-Open 立刻放全部流量,可能又把它打挂。放一个请求是「最小试探」——既验证恢复,又不冲击。
  • 自动恢复:整个过程无需人工干预——下游修好了,Half-Open 试探成功,自动回 Closed;没修好,继续 Open 等下一轮。运维只需关注监控告警。
  • healthCheckFunc:可配置自定义健康检查(如调下游的 /health 端点),Half-Open 时先跑健康检查,失败则直接回 Open 不试真实调用,减少对下游的冲击。

四、配置项全景与调参建议

配置项默认值含义调参建议
timeout10000 (10s)单次调用超时(ms),设 false 禁用生产调小到秒级(1-5s),别用默认 10s
errorThresholdPercentage50错误率跳闸阈值(%)严格场景 30、宽松场景 60,看业务容忍度
resetTimeout30000 (30s)Open 后多久试探(ms)下游恢复快的 10s、慢的 60s
rollingCountTimeout10000 (10s)统计窗口长度(ms)短窗口响应快、长窗口平滑
rollingCountBuckets10窗口分桶数= rollingCountTimeout/1000 通常合适
volumeThreshold0最小调用量才统计生产必设非零(如 5),防低调用量误判
maxCapacity无限最大并发调用数防并发洪流,配合信号量限流
enabledtrue是否启用熔断故障排查时可临时 false 关闭
cachefalse是否缓存成功结果适合读多写少的幂等调用

调参误区

  • timeout 用默认 10s:太长,下游慢时上游资源被占满,失去保护意义。生产建议 1-5s。
  • volumeThreshold 留默认 0:调 1 次失败就跳闸,正常抖动误熔断。生产必设非零。
  • errorThresholdPercentage 设太低(如 10):正常抖动就跳闸,频繁误熔断。建议 30-50 起步。

五、事件清单与监控集成

Opossum 熔断器是 EventEmitter,所有关键行为都发事件:

javascript
const breaker = new CircuitBreaker(fn, options);

// 调用生命周期
breaker.on('fire', (args) => log('发起调用', args));
breaker.on('success', (result) => log('成功', result));
breaker.on('failure', (err) => log('失败', err));
breaker.on('timeout', () => log('超时'));

// 熔断器状态
breaker.on('open', () => alert('熔断器打开!'));    // Closed→Open
breaker.on('close', () => log('熔断器恢复'));        // Half-Open→Closed
breaker.on('halfOpen', () => log('开始试探恢复'));   // Open→Half-Open
breaker.on('reject', () => log('请求被熔断拒绝'));

// 降级与限流
breaker.on('fallback', (result) => log('触发降级', result));
breaker.on('semaphoreLocked', () => log('并发达上限被拒'));

// 健康检查
breaker.on('healthCheckFailed', () => log('健康检查失败'));
breaker.on('shutdown', () => log('熔断器关闭'));
  • 监控集成opossum-prometheus 模块把熔断器状态导出 Prometheus 指标(状态、调用数、错误率、熔断次数),接入 Grafana 做大盘。
  • 告警规则:监听 open 事件 → 触发告警(邮件/钉钉/PagerDuty)——熔断器打开意味着下游出问题,运维要介入。

下一步

熔断器原理讲完后,下一步是容错模式与对比——超时、重试、限流、舱壁隔离如何与熔断器配合,以及 Opossum 与 circuit-breaker-js、Resilience4j 的对比。