3. 熔断器模式原理:三状态与状态转换机制

熔断器模式(Circuit Breaker Pattern)是微服务架构中防止级联故障的核心手段。其灵感来源于电路中的断路器:当电流异常时自动断开,保护整个系统不被烧毁。在分布式系统中,熔断器监控远程调用(如 HTTP、RPC)的失败率,一旦达到阈值,立即切断后续请求,避免资源被无效等待耗尽。

3.1 熔断器三状态模型

熔断器内部维护一个有限状态机,包含三个核心状态:关闭(Closed)、打开(Open)、半开(Half-Open)。每个状态对应不同的请求处理策略和内部计数器行为。

状态 含义 请求处理方式 内部计数器
关闭(Closed) 熔断器未触发,服务正常 请求正常转发到目标服务 累计失败次数/失败率
打开(Open) 熔断器已触发,服务不可用 请求直接返回降级响应(Fallback) 计时器开始倒计时(超时窗口)
半开(Half-Open) 试探性恢复,允许少量请求通过 放行有限数量的探测请求 记录探测请求的成功/失败
关键认知: 熔断器不是“永久关闭”服务,而是通过状态转换实现自动恢复。半开状态是系统自愈能力的核心设计。

3.2 状态转换机制与触发条件

状态转换由三个关键事件驱动:失败阈值到达、超时窗口到期、探测请求结果。下图展示了完整的状态转换流程:

熔断器三状态转换机制 关闭 Closed 打开 Open 半开 Half-Open 失败率 ≥ 阈值 超时窗口到期 探测成功 → 重置计数器 → 关闭 探测失败 → 立即打开 状态转换规则总结 • 关闭→打开:失败次数或失败率超过配置阈值 • 打开→半开:等待超时窗口(如 5 秒)后自动进入 • 半开→关闭:探测请求成功,系统恢复

3.3 熔断阈值与超时配置详解

熔断器的行为由一组可配置参数决定。在 NestJS 中,通常使用 @nestjs/bull 或 @nestjs/microservices 结合自定义拦截器实现,但核心参数模型是通用的。

参数名称 类型 默认值 说明
failureThreshold number 5 连续失败次数阈值,超过后熔断器打开
failureRateThreshold number 50 滑动窗口内失败率百分比阈值(如 50%)
slidingWindowSize number 10 滑动窗口大小(请求数),用于计算失败率
openTimeout number 5000 打开状态持续时间(毫秒),之后进入半开
halfOpenMaxRequests number 3 半开状态下允许通过的探测请求数量
halfOpenSuccessThreshold number 2 半开状态下连续成功次数,达到后关闭熔断器
实践建议: 在 NestJS 中,建议将熔断器配置放在环境变量或配置中心(如 Consul/Nacos)中,方便动态调整。生产环境推荐 failureThreshold 设为 5~10,openTimeout 设为 5~15 秒,避免频繁状态震荡。

3.4 滑动窗口与失败率计算

熔断器通常使用滑动窗口(Sliding Window)来统计失败率,而不是全局累计。滑动窗口有两种常见实现:

  • 计数滑动窗口: 维护最近 N 个请求的环形缓冲区,每次新请求覆盖最旧记录。窗口大小固定,内存占用稳定。
  • 时间滑动窗口: 将时间划分为多个桶(如 1 秒一个桶),只统计最近 M 秒内的请求。适用于高并发场景,减少锁竞争。

以下是一个基于计数滑动窗口的失败率计算伪代码:

class SlidingWindowCounter {
  private windowSize: number;      // 窗口大小(请求数)
  private failureThreshold: number; // 失败次数阈值
  private buffer: boolean[];       // true=成功, false=失败
  private index: number = 0;
  private failureCount: number = 0;

  record(success: boolean): void {
    const old = this.buffer[this.index];
    if (old !== undefined && !old) this.failureCount--;
    this.buffer[this.index] = success;
    if (!success) this.failureCount++;
    this.index = (this.index + 1) % this.windowSize;
  }

  isOpen(): boolean {
    return this.failureCount >= this.failureThreshold;
  }
}

3.5 状态转换的完整时序流程

下面用一张时序图展示熔断器从正常到熔断再到恢复的完整生命周期:

熔断器完整生命周期时序 时间→ 关闭状态 请求正常通过,失败计数递增 打开状态 请求直接降级,计时器启动 半开状态 放行探测请求,评估结果 失败率≥阈值 超时到期 探测成功 → 重置计数器 → 回到关闭 探测失败 关键设计原则 • 半开状态是系统自愈的唯一入口,必须谨慎控制探测请求数量 • 打开状态下所有请求走降级逻辑,避免对下游造成二次压力

3.6 配置示例:NestJS 中的熔断器参数

在 NestJS 项目中,推荐使用 @nestjs/bull 的 CircuitBreaker 装饰器或自定义拦截器。以下是一个基于 @nestjs/common 拦截器的配置示例:

// circuit-breaker.config.ts
export interface CircuitBreakerConfig {
  failureThreshold: number;        // 连续失败次数阈值
  failureRateThreshold: number;    // 失败率阈值(百分比)
  slidingWindowSize: number;       // 滑动窗口大小
  openTimeout: number;             // 打开状态超时(毫秒)
  halfOpenMaxRequests: number;     // 半开状态最大探测请求数
  halfOpenSuccessThreshold: number;// 半开状态连续成功阈值
}

export const defaultCircuitBreakerConfig: CircuitBreakerConfig = {
  failureThreshold: 5,
  failureRateThreshold: 50,
  slidingWindowSize: 10,
  openTimeout: 5000,
  halfOpenMaxRequests: 3,
  halfOpenSuccessThreshold: 2,
};
避坑指南: 不要将 openTimeout 设置过短(如 < 1 秒),否则熔断器会在打开和半开之间频繁震荡,导致系统不稳定。建议至少 3 秒以上,并配合指数退避策略。

3.7 本章小结

  • 熔断器三状态(关闭、打开、半开)是分布式系统自我保护的基础模型。
  • 状态转换由失败阈值、超时窗口、探测结果三个事件驱动。
  • 滑动窗口机制确保失败率统计的时效性和准确性。
  • 配置参数需根据业务场景调整,避免过于敏感或过于迟钝。