2. Circuit Breaker 核心概念:Closed、Open、Half-Open 三种状态的定义与转换条件
Circuit Breaker(断路器)模式的核心是一个有限状态机,它通过 Closed(关闭)、Open(断开) 和 Half-Open(半开) 三种状态来管理对外部服务(或资源)的调用。状态机的设计目标是:在系统正常时最小化开销,在系统故障时快速熔断,并在系统恢复时自动探测。
2.1 三种状态的定义
| 状态 | 英文名称 | 定义 | 行为特征 |
|---|---|---|---|
| 关闭 | Closed | 断路器处于初始或正常状态,允许请求通过。 | 请求正常转发给下游服务;统计失败率;失败率未超阈值时保持此状态。 |
| 断开 | Open | 断路器因失败率超过阈值而跳闸,拒绝所有请求。 | 请求立即失败(快速失败),不调用下游;等待超时时间后进入半开状态。 |
| 半开 | Half-Open | 断路器在断开超时后进入的试探状态,允许有限数量的探测请求通过。 | 放行少量请求测试下游健康;成功则关闭,失败则重新断开。 |
关键理解: 三种状态形成了一个闭环的自我保护机制。Closed 是“信任”,Open 是“惩罚”,Half-Open 是“试探”。状态机的核心价值在于:防止级联故障 和 自动恢复。
2.2 状态转换条件与流程图
状态转换由三个核心参数驱动:失败阈值(如失败次数或失败率)、重置超时时间(从 Open 到 Half-Open 的等待时间)、探测请求数(Half-Open 状态下允许的最大请求数)。下图展示了完整的状态转换路径:
2.3 状态转换条件详解
- Closed → Open:当在滑动时间窗口内(如 10 秒),请求失败率超过预设阈值(如 50%),或连续失败次数达到阈值(如 5 次),断路器从 Closed 跳转到 Open。此时所有后续请求立即失败,不再调用下游。
- Open → Half-Open:断路器在 Open 状态停留一段预设的 重置超时时间(如 30 秒)后,自动进入 Half-Open 状态。这是系统给下游服务的一个“恢复机会”。
- Half-Open → Closed:在 Half-Open 状态下,断路器允许有限数量的探测请求(如 3 个)通过。如果这些探测请求全部成功,则认为下游已恢复,断路器回到 Closed 状态,计数器重置。
- Half-Open → Open:在 Half-Open 状态下,如果任何一个探测请求失败,断路器立即回到 Open 状态,并重新开始计时重置超时时间。
常见陷阱: 不要将 Half-Open 状态视为“半恢复”状态。Half-Open 是一个高敏感度的试探状态,任何失败都会导致立即回到 Open。同时,Half-Open 状态下通过的请求数必须严格控制(通常为 1~3 个),否则可能在下游尚未完全恢复时造成二次冲击。
2.4 状态转换参数配置示例
在 Go 语言实现中,状态转换参数通常在初始化时配置。以下是一个典型的配置结构:
type CircuitBreakerConfig struct {
// 失败阈值:滑动窗口内失败率达到此比例时触发 Open
FailureRateThreshold float64 // 例如 0.5 (50%)
// 滑动窗口大小:统计失败率的时间窗口(秒)
SlidingWindowSize int // 例如 10
// 最小请求数:滑动窗口内至少需要这么多请求才计算失败率
MinRequestCount int // 例如 5
// 重置超时:从 Open 到 Half-Open 的等待时间(秒)
ResetTimeout time.Duration // 例如 30 * time.Second
// 半开最大探测请求数:Half-Open 状态下允许的最大请求数
HalfOpenMaxRequests int // 例如 3
}
2.5 状态转换的数学逻辑
状态转换可以用简单的伪代码逻辑表示:
// 每次请求完成后调用
func onRequestComplete(success bool) {
switch state {
case Closed:
updateMetrics(success)
if failureRate >= config.FailureRateThreshold &&
totalRequests >= config.MinRequestCount {
transitionTo(Open)
}
case Open:
// 所有请求直接失败,不更新指标
if timeSinceLastTransition >= config.ResetTimeout {
transitionTo(HalfOpen)
}
case HalfOpen:
if success {
successfulProbes++
if successfulProbes >= config.HalfOpenMaxRequests {
resetMetrics()
transitionTo(Closed)
}
} else {
transitionTo(Open)
}
}
}
压测验证要点: 在压测中,需要重点验证以下边界条件:
• 失败率刚好达到阈值时是否准确触发 Open。
• 重置超时时间到达后是否准时进入 Half-Open。
• Half-Open 状态下探测请求成功/失败后的状态转换是否立即生效。
• 高并发场景下状态转换的原子性(避免竞态条件导致状态不一致)。
• 失败率刚好达到阈值时是否准确触发 Open。
• 重置超时时间到达后是否准时进入 Half-Open。
• Half-Open 状态下探测请求成功/失败后的状态转换是否立即生效。
• 高并发场景下状态转换的原子性(避免竞态条件导致状态不一致)。
2.6 三种状态的行为对比
| 对比维度 | Closed | Open | Half-Open |
|---|---|---|---|
| 请求是否通过 | 是 | 否(快速失败) | 有限通过(探测) |
| 是否统计失败率 | 是 | 否 | 是(仅统计探测请求) |
| 对下游的影响 | 正常调用 | 无调用(保护下游) | 少量调用(试探) |
| 触发下一状态的条件 | 失败率超阈值 | 超时时间到达 | 探测成功/失败 |
| 典型持续时间 | 不确定(正常时无限长) | 固定超时(如 30s) | 短(通常 1~3 个请求) |