3. Closed 状态详解:正常调用流程、计数器机制、滑动窗口算法原理

Closed 状态是 Circuit Breaker 的初始状态,也是系统正常运行时的默认状态。在此状态下,断路器允许所有请求通过,并持续监控请求的成功/失败比率。当失败率超过预设阈值时,断路器将触发状态转换至 Open 状态。理解 Closed 状态的核心机制,是进行性能调优和压测分析的基础。

3.1 正常调用流程

在 Closed 状态下,每一次外部调用都遵循以下标准流程:

  1. 请求到达:客户端发起远程调用(如 RPC、HTTP 请求)。
  2. 计数器记录:断路器拦截请求,将本次调用计入“总请求数”计数器。
  3. 执行调用:请求被转发至下游服务或资源。
  4. 结果判定:根据调用结果(成功/失败/超时/异常),更新对应的成功或失败计数器。
  5. 阈值检查:每次调用结束后,断路器检查当前时间窗口内的失败率是否超过阈值。
  6. 状态维持或转换:若失败率低于阈值,保持 Closed 状态;若超过阈值,则切换至 Open 状态。
压测关注点: 在压测场景中,Closed 状态下的调用延迟、计数器更新开销以及阈值检查的计算耗时,都会影响系统的吞吐量。通常建议将计数器的更新操作设计为无锁或轻量级 CAS 操作,避免成为性能瓶颈。

3.2 计数器机制

计数器是 Closed 状态最基础的实现方式。它维护两个核心指标:

计数器类型 描述 典型实现
请求总数计数器 记录当前时间窗口内的总调用次数 AtomicLong 或 LongAdder
失败次数计数器 记录当前时间窗口内的失败调用次数 AtomicLong 或 LongAdder

计数器机制的判定逻辑如下:

// 伪代码:计数器模式阈值判定
long totalCount = requestCounter.get();
long failCount = failureCounter.get();

// 只有当总请求数达到最小请求数时,才进行阈值判定
if (totalCount >= MINIMUM_REQUEST_THRESHOLD) {
    double failureRate = (double) failCount / totalCount;
    if (failureRate > FAILURE_RATE_THRESHOLD) {
        // 切换到 Open 状态
        stateTransition(State.OPEN);
    }
}
性能陷阱: 简单的计数器机制存在“毛刺效应”。例如,在 1 秒窗口内,前 900ms 请求全部成功,最后 100ms 突发 10 个请求全部失败,此时失败率可能瞬间达到 100%,导致断路器误触发。滑动窗口算法正是为了解决此问题而设计。

3.3 滑动窗口算法原理

滑动窗口算法将时间划分为多个细粒度的时间片(Bucket),每个 Bucket 独立记录该时间段内的请求数据。窗口随时间向前滑动,丢弃过期 Bucket 的数据,从而更平滑地反映最近的系统状态。

滑动窗口算法原理(10 Bucket 示例) T₀ T₁₀ 当前滑动窗口(1 秒 = 10 个 Bucket) 过期 过期 过期 成功:8 失败:1 成功:10 失败:0 成功:9 失败:1 成功:5 失败:5 成功:7 失败:2 成功:10 失败:0 成功:8 失败:2 时间滑动方向 → 窗口内总请求数 = 7 个 Bucket 之和;失败率 = 失败总数 / 总请求数

3.4 滑动窗口 vs 计数器:性能与准确性对比

对比维度 简单计数器 滑动窗口
内存开销 低(仅 2 个计数器) 较高(N 个 Bucket,每个 Bucket 含计数数据)
计算开销 低(每次请求 O(1) 更新) 中等(需维护 Bucket 轮换,O(1) 更新 + 定期清理)
毛刺敏感度 高(短时突发易导致误判) 低(数据平滑,反映真实趋势)
实时性 高(立即反映最新计数) 高(窗口滑动粒度可调)
适用场景 低并发、对毛刺不敏感的系统 高并发、对稳定性要求高的生产环境

3.5 滑动窗口的 Bucket 轮换策略

滑动窗口的核心在于 Bucket 的创建与过期管理。常见的实现策略有两种:

  • 定时轮换:使用后台线程定期(如每 100ms)创建新 Bucket,并丢弃最旧的 Bucket。实现简单,但存在时间片对齐问题。
  • 懒创建 + 时间戳对齐:仅在请求到达时检查当前时间戳,若已进入新的时间片则创建新 Bucket。更精确,但需处理并发创建问题。
压测调优建议: 在压测环境中,建议将 Bucket 数量设置为 10(即窗口被分为 10 个时间片)。Bucket 数量过少会导致平滑效果不足;数量过多(如 100 个)则会增加内存开销和 GC 压力。对于 1 秒窗口,每个 Bucket 100ms 是一个经过实践验证的平衡点。
Closed 状态:滑动窗口阈值判定流程 请求到达 写入当前 Bucket 计算窗口失败率 失败率 > 阈值? 是 切换到 Open 否 保持 Closed 状态 继续处理下一个请求

3.6 压测场景下的关键参数配置

在性能压测中,Closed 状态的以下参数直接影响系统行为和稳定性:

参数名称 推荐压测初始值 调优方向
滑动窗口大小 1 秒 窗口越大,数据越平滑,但响应变慢;窗口越小,响应快但易受毛刺影响
Bucket 数量 10 增加 Bucket 提升精度,但增加内存开销;减少则降低精度
失败率阈值 50% 根据业务容忍度调整:核心服务建议 20%-30%,非核心可放宽至 60%
最小请求数 20 避免在低流量时因样本不足导致误判;高并发系统可适当提高
压测实战技巧: 在压测初期,建议将最小请求数设置为 0,以便观察断路器在低流量下的行为。待系统稳定后,再根据实际流量调整该参数。同时,监控 Bucket 的创建和回收频率,确保 GC 压力在可接受范围内。