3. 半开状态核心原理
半开状态(Half-Open)是熔断器从“开”到“关”的过渡状态,也是熔断器能够自动恢复的关键设计。没有半开状态,熔断器一旦打开就只能由人工重置,无法实现自愈。
3.1 半开状态的设计目的
半开状态要解决的核心矛盾是:服务可能已经恢复,但熔断器不敢直接放行全部流量。直接放行会导致两个问题:
- 雪崩重现:如果服务仍未恢复,大量请求涌入会再次压垮服务。
- 恢复探测缺失:没有试探机制,熔断器永远不知道下游是否已恢复。
半开状态通过限制探测流量,在“保护系统”和“尽快恢复”之间取得平衡。其核心设计目标如下表所示:
| 设计目标 | 说明 | 实现方式 |
|---|---|---|
| 安全探测 | 用最小代价试探下游是否恢复 | 只放行少量请求,不造成冲击 |
| 快速自愈 | 一旦确认恢复,立即关闭熔断器 | 连续成功达到阈值后自动关闭 |
| 避免抖动 | 防止频繁开关导致系统震荡 | 半开状态有超时机制,失败后重新打开 |
| 透明切换 | 调用方无需感知状态变化 | 熔断器内部状态机自动管理 |
关键认知:半开状态不是“半死不活”,而是“试探性恢复”。它是熔断器从故障态回到正常态的唯一通道。
3.2 允许有限请求通过
在半开状态下,熔断器不会放行所有请求,而是按照预设的探测窗口大小(通常由参数 permittedNumberOfCallsInHalfOpenState 控制)放行固定数量的请求。这些请求被称为“探测请求”或“探针请求”。
探测请求的通过规则:
- 计数窗口:熔断器维护一个原子计数器,每放行一个请求,计数器 +1。
- 上限约束:当计数器达到
permittedNumberOfCallsInHalfOpenState时,后续请求直接快速失败(抛出CallNotPermittedException)。 - 滑动重置:如果探测请求全部成功,熔断器关闭;如果有任何失败,熔断器重新打开并重置计数器。
以 Resilience4j 为例,半开状态下的请求通过逻辑如下:
// Resilience4j 半开状态请求许可判断(简化核心逻辑)
if (state == State.HALF_OPEN) {
// 原子递增,确保并发安全
long currentCount = halfOpenCallCounter.incrementAndGet();
if (currentCount <= permittedNumberOfCallsInHalfOpenState) {
// 允许请求通过
return true;
} else {
// 超过探测窗口上限,拒绝
return false;
}
}
实践建议:探测窗口大小不宜过大(通常 3~10 个请求),否则可能对正在恢复的服务造成压力;也不宜过小(1 个请求),否则单次成功可能掩盖间歇性故障。
3.3 探测服务恢复的机制
半开状态通过连续成功/失败判定来探测服务是否恢复。判定逻辑通常包含两个关键参数:
- 成功阈值(
successThresholdInHalfOpenState):连续成功次数达到此值,熔断器关闭。 - 失败阈值(
failureThresholdInHalfOpenState):连续失败次数达到此值,熔断器重新打开。
探测流程如下图所示:
避坑提示:半开状态的探测请求必须与正常业务请求同构——即使用相同的超时、重试和序列化配置。如果探测请求使用简化参数而正常请求使用复杂参数,探测结果可能失真,导致熔断器误判。
3.4 半开状态参数配置对照
不同熔断器框架对半开状态的参数命名略有差异,但核心语义一致。下表列出主流框架的对应关系:
| 参数含义 | Resilience4j | Hystrix | Sentinel |
|---|---|---|---|
| 半开状态允许的请求数 | permittedNumberOfCallsInHalfOpenState |
circuitBreaker.requestVolumeThreshold(复用) |
halfOpenMaxRequestCount |
| 半开状态成功阈值 | successThresholdInHalfOpenState |
固定为 1(单次成功即关闭) | halfOpenSuccessRatio(比例模式) |
| 半开状态失败阈值 | failureThresholdInHalfOpenState |
固定为 1(单次失败即打开) | halfOpenFailRatio(比例模式) |
| 半开状态超时时间 | waitDurationInOpenState(进入半开前等待) |
sleepWindowInMilliseconds |
halfOpenTimeout |
配置原则:半开状态的探测窗口大小建议设置为正常并发量的 1%~5%。例如,正常 QPS 为 1000 的服务,探测窗口设为 5~10 个请求即可。过大的窗口会抵消熔断的保护效果。