3. 半开状态核心原理

半开状态(Half-Open)是熔断器从“开”到“关”的过渡状态,也是熔断器能够自动恢复的关键设计。没有半开状态,熔断器一旦打开就只能由人工重置,无法实现自愈。

3.1 半开状态的设计目的

半开状态要解决的核心矛盾是:服务可能已经恢复,但熔断器不敢直接放行全部流量。直接放行会导致两个问题:

  • 雪崩重现:如果服务仍未恢复,大量请求涌入会再次压垮服务。
  • 恢复探测缺失:没有试探机制,熔断器永远不知道下游是否已恢复。

半开状态通过限制探测流量,在“保护系统”和“尽快恢复”之间取得平衡。其核心设计目标如下表所示:

设计目标 说明 实现方式
安全探测 用最小代价试探下游是否恢复 只放行少量请求,不造成冲击
快速自愈 一旦确认恢复,立即关闭熔断器 连续成功达到阈值后自动关闭
避免抖动 防止频繁开关导致系统震荡 半开状态有超时机制,失败后重新打开
透明切换 调用方无需感知状态变化 熔断器内部状态机自动管理
关键认知:半开状态不是“半死不活”,而是“试探性恢复”。它是熔断器从故障态回到正常态的唯一通道。

3.2 允许有限请求通过

在半开状态下,熔断器不会放行所有请求,而是按照预设的探测窗口大小(通常由参数 permittedNumberOfCallsInHalfOpenState 控制)放行固定数量的请求。这些请求被称为“探测请求”或“探针请求”。

探测请求的通过规则:

  1. 计数窗口:熔断器维护一个原子计数器,每放行一个请求,计数器 +1。
  2. 上限约束:当计数器达到 permittedNumberOfCallsInHalfOpenState 时,后续请求直接快速失败(抛出 CallNotPermittedException)。
  3. 滑动重置:如果探测请求全部成功,熔断器关闭;如果有任何失败,熔断器重新打开并重置计数器。

以 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):连续失败次数达到此值,熔断器重新打开。

探测流程如下图所示:

半开状态探测恢复流程图 熔断器进入半开状态 放行有限探测请求(如 5 个) 请求结果? 成功 / 失败 成功 连续成功计数 +1 达到成功 阈值? 是 关闭熔断器 否 继续放行 失败 连续失败计数 +1 达到失败 阈值? 是 重新打开 否 继续放行
避坑提示:半开状态的探测请求必须与正常业务请求同构——即使用相同的超时、重试和序列化配置。如果探测请求使用简化参数而正常请求使用复杂参数,探测结果可能失真,导致熔断器误判。

3.4 半开状态参数配置对照

不同熔断器框架对半开状态的参数命名略有差异,但核心语义一致。下表列出主流框架的对应关系:

参数含义 Resilience4j Hystrix Sentinel
半开状态允许的请求数 permittedNumberOfCallsInHalfOpenState circuitBreaker.requestVolumeThreshold(复用) halfOpenMaxRequestCount
半开状态成功阈值 successThresholdInHalfOpenState 固定为 1(单次成功即关闭) halfOpenSuccessRatio(比例模式)
半开状态失败阈值 failureThresholdInHalfOpenState 固定为 1(单次失败即打开) halfOpenFailRatio(比例模式)
半开状态超时时间 waitDurationInOpenState(进入半开前等待) sleepWindowInMilliseconds halfOpenTimeout
配置原则:半开状态的探测窗口大小建议设置为正常并发量的 1%~5%。例如,正常 QPS 为 1000 的服务,探测窗口设为 5~10 个请求即可。过大的窗口会抵消熔断的保护效果。