熔断器基本原理与状态机
熔断器这东西,最早是从电路保护里借来的概念。你想想看,家里的电闸跳了,是因为电流过大烧保险丝。换成微服务,就是当调用链路出现大量失败或超时时,主动把“开关”断开,别让请求继续过去添乱。
我刚开始接触熔断降级时,总觉得这玩意儿跟限流差不多。后来在线上踩过坑才明白——限流是保护自己不被撑爆,熔断是保护调用方不被拖死。两码事。
状态机:三个状态搞定一切
几乎所有成熟的熔断器实现,都遵循一个三状态模型:
- 关闭(Closed)——正常干活,请求照常放行。同时偷偷统计失败率。
- 打开(Open)——熔断了,直接拒绝请求,快速失败。给你一个喘息窗口。
- 半开(Half-Open)——试探阶段,放少量请求过去看看服务恢复了没。
状态流转不是随便跳的,有明确条件。我把这套逻辑画成了一张图,你一看就明白。
状态转换的详细条件
光看状态机图还不够,咱们得把每个跳转条件掰扯清楚。
| 转换 | 触发条件 | 典型参数 | 我的备注 |
|---|---|---|---|
| Closed → Open | 统计周期内失败率超过阈值 | 失败率 > 50% 至少请求数 > 10 |
阈值别设太低,否则容易误触 |
| Open → Half-Open | 熔断超时窗口结束(时间到达) | 超时=5s ~ 30s | 我一般用 10s,抖动场景可加长 |
| Half-Open → Closed | 试探请求全部成功 | 试探数=3,成功率=100% | 通过后立即关闭,不必等窗口 |
| Half-Open → Open | 试探请求中有一个失败 | 重新计算超时窗口 | 注意:失败就立刻回 Open,不等 |
小技巧:我曾在一个高并发支付项目里,把半开试探数从1改成3。结果发现3个请求中偶尔有一个超时,其他俩成功,结果又回到了Open。后来改成“半数成功即可”的策略,稳定多了。
统计窗口:熔断的“记忆”有多大
熔断器不是把失败率一直累加,那样老数据会影响判断。一般用滑动窗口统计——留最近一段时间的数据。
- 时间窗口(Time Window):比如统计过去10秒内的请求。10秒一过,旧数据就丢。
- 计数窗口(Count Window):统计最近N次请求。N设定好,新旧交替。
- 混合窗口:同时限制时间和次数,取其中。
我个人习惯用时间窗口,因为线上调用量变化大,单纯按次数容易在小流量时误判。
注意坑:窗口太小容易频繁触发熔断,窗口太大又反应迟钝。我曾经把窗口设成60秒,结果服务挂了一分钟熔断器都没动作——那叫一个惨。
恢复机制的两种流派
状态机里的 Half-Open 就是恢复入口。但具体怎么恢复,业界有两种主流做法:
- 被动恢复(超时驱动):Open 状态保持固定时间,时间一到自动切到 Half-Open 试探。常用方案。简单,但固定时长不灵活。
- 自适应恢复(指数退避):每次失败后,下次超时窗口翻倍。比如第一次5s,第二次10s,第三次20s……
我做过一个对比测试:在流量抖动频繁的场景下,自适应恢复比固定超时减少了30%的误熔断。代价是代码复杂一点。
// 自适应恢复的核心逻辑(伪代码)
if currentState == OPEN:
backoffDuration = baseTimeout * (2 ^ failCount)
if elapsedSinceFuse > backoffDuration:
transitionTo(HALF_OPEN)
else:
rejectRequest()
核心结论:状态机是熔断器的骨架,统计窗口是血肉,恢复策略是神经。三者配合不好,熔断就会变成“阉割”或者“暴毙”。
熔断器 vs 断路器:别混了
你可能会在资料里看到“断路器”这个词。其实一回事,英文都是 Circuit Breaker。但在中文语境里,微服务界说“熔断器”更多,电路界说“断路器”更多。我讲课都统一叫熔断器,省得混淆。
最后送你一句我自己的总结:熔断器不是降级本身,它是降级的开关。开关坏了,降级就是空话。