2. 熔断器三种状态详解:Closed、Open、Half-Open 的状态转换机制与触发条件
熔断器(Circuit Breaker)是分布式系统中防止级联故障的核心模式。其本质是一个有限状态机,通过三个关键状态——Closed(关闭)、Open(打开)、Half-Open(半开)——以及它们之间的转换规则,实现对下游依赖的保护与自动恢复。理解这三种状态及其触发条件是设计自愈剧本的基础。
2.1 状态总览与核心对比
下表从请求处理行为、健康判断依据和典型触发条件三个维度,对三种状态进行横向对比:
| 状态 | 请求处理行为 | 健康判断依据 | 典型触发条件 |
|---|---|---|---|
| Closed(关闭) | 正常转发请求到下游,实时统计失败率 | 滑动窗口内的失败率 < 阈值 | 系统初始化、故障恢复后重置 |
| Open(打开) | 直接拒绝请求(快速失败),不调用下游 | 失败率 ≥ 阈值,触发熔断 | 连续失败次数/失败率超过预设阈值 |
| Half-Open(半开) | 允许有限数量的探测请求通过 | 探测请求的成功/失败结果 | Open 状态持续达到超时时间 |
设计要点: Closed 状态是系统的“正常态”,Open 是“保护态”,Half-Open 是“试探恢复态”。三个状态形成一个闭环,确保系统在故障时快速隔离,在恢复时平滑回归。
2.2 状态转换机制与触发条件详解
2.2.1 Closed → Open:熔断触发
当熔断器处于 Closed 状态时,所有请求正常通过,同时熔断器会使用滑动窗口(如固定时间窗口或滑动时间窗口)统计请求的成功/失败情况。当统计结果满足以下任一条件时,状态从 Closed 切换到 Open:
- 失败率阈值触发: 在滑动窗口内(例如最近 10 秒),请求失败率超过预设阈值(例如 50%)。
- 连续失败次数触发: 连续 N 个请求全部失败(例如连续 5 次超时或返回 5xx)。
- 最小请求量保护: 滑动窗口内的总请求数必须达到最小样本量(例如 10 个请求),避免因少量请求的偶然失败而误触发熔断。
避坑指南: 不要仅依赖失败率阈值。在低流量场景下,少量失败可能导致失败率瞬间飙升,从而误触发熔断。务必配合“最小请求量”条件一起使用。
2.2.2 Open → Half-Open:恢复试探
进入 Open 状态后,熔断器会启动一个熔断超时计时器(也称为休眠窗口)。在超时时间内,所有请求直接返回降级响应(如抛出异常或返回缓存数据),不调用下游。当计时器到期时,状态自动切换到 Half-Open:
- 触发条件: Open 状态的持续时间 ≥ 预设的熔断超时时间(例如 30 秒)。
- 设计意图: 给下游服务一个“冷却期”,让其有机会从故障中恢复(如重启、流量积压被消化)。
- 超时时间策略: 固定超时(简单但不够灵活) vs. 指数退避超时(每次熔断超时时间翻倍,防止频繁试探)。
2.2.3 Half-Open → Closed 或 Open:试探决策
Half-Open 是熔断器的“决策窗口”。在此状态下,熔断器会放行有限数量的探测请求(例如 3 个并发请求),并根据这些请求的结果决定下一步状态:
- Half-Open → Closed(恢复成功):
- 触发条件:所有探测请求全部成功(或成功率 ≥ 恢复阈值)。
- 行为:重置失败计数器,关闭熔断器,恢复正常流量转发。
- Half-Open → Open(恢复失败):
- 触发条件:任意一个探测请求失败(或失败率超过阈值)。
- 行为:立即重新打开熔断器,重置熔断超时计时器(通常使用指数退避策略增加下次等待时间)。
最佳实践: Half-Open 状态的探测请求数量应远小于正常流量,避免在试探阶段对下游造成过大压力。同时,建议对探测请求设置更短的超时时间,以快速获得结果。
2.3 状态转换流程图
下图展示了熔断器三种状态之间的完整转换路径,以及每个转换的触发条件:
2.4 状态转换的代码示例(伪代码)
以下伪代码展示了熔断器状态机的核心转换逻辑,重点体现触发条件的判断:
class CircuitBreaker {
state = CLOSED
failureCount = 0
successCount = 0
lastFailureTime = 0
halfOpenProbeCount = 0
// 配置参数
failureThreshold = 5 // 连续失败次数阈值
failureRateThreshold = 0.5 // 失败率阈值(50%)
minRequestCount = 10 // 最小请求样本量
timeout = 30000 // 熔断超时时间(毫秒)
halfOpenMaxProbes = 3 // 半开状态最大探测请求数
function call(request) {
if (state == OPEN) {
if (currentTime - lastFailureTime > timeout) {
state = HALF_OPEN
halfOpenProbeCount = 0
} else {
return fallbackResponse() // 快速失败
}
}
if (state == HALF_OPEN) {
if (halfOpenProbeCount >= halfOpenMaxProbes) {
return fallbackResponse() // 探测请求已满,等待结果
}
halfOpenProbeCount++
}
// 转发请求到下游
response = sendRequest(request)
if (response.isSuccess()) {
if (state == HALF_OPEN) {
// 探测成功:重置计数器,关闭熔断器
resetCounters()
state = CLOSED
}
recordSuccess()
} else {
recordFailure()
if (state == HALF_OPEN) {
// 探测失败:立即重新打开熔断器
state = OPEN
lastFailureTime = currentTime
timeout = min(timeout * 2, maxTimeout) // 指数退避
} else if (state == CLOSED) {
// 检查是否满足熔断条件
if (failureCount >= failureThreshold ||
(totalRequestCount >= minRequestCount &&
failureRate >= failureRateThreshold)) {
state = OPEN
lastFailureTime = currentTime
}
}
}
}
}
2.5 状态转换的关键设计参数
在实际的熔断器实现中,以下参数直接影响状态转换的灵敏度和系统的稳定性:
| 参数名称 | 所属状态转换 | 推荐初始值 | 调优建议 |
|---|---|---|---|
| 失败率阈值 | Closed → Open | 50% | 对延迟敏感的服务可降低至 30% |
| 连续失败次数 | Closed → Open | 5 次 | 根据下游并发度调整,避免误触发 |
| 最小请求样本量 | Closed → Open | 10 个 | 低流量服务可降低至 5 个 |
| 熔断超时时间 | Open → Half-Open | 30 秒 | 结合下游平均恢复时间(MTTR)设置 |
| 探测请求数量 | Half-Open 决策 | 3 个 | 不超过正常并发量的 10% |
| 指数退避倍数 | Half-Open → Open | 2 倍 | 最大超时时间建议不超过 300 秒 |
调优原则: 熔断器参数没有“万能值”。建议在混沌工程实验中,通过注入不同级别的故障(如延迟故障、异常故障),观察熔断器的状态转换行为,逐步调整参数至最优。