Skip to content

能力:熔断守护(Circuit Breaker)

接口持续失败时自动熔断,避免无意义请求打爆服务端,支持半开试探自动恢复。

工作原理

正常状态 (NORMAL) → 连续失败达到阈值 → 熔断状态 (CIRCUIT_BREAKER)
                                              ↓ 等待 recoverDelay
                                        半开状态 (HALF_OPEN)
                                              ↓ 试探请求成功
                                        正常状态 (NORMAL)

默认仅网络错误和 5xx 计入失败;ERR_CANCELEDAbortErrorABORT_ERR 和 Axios legacy cancel 不计失败,半开取消也不会被判定为恢复成功。

单业务场景配置

全局开启熔断保护

javascript
const requestManager = setupRequestGuard(axios, {
  defaults: {
    circuitBreaker: {
      enabled: true,
      type: 'request',        // 按接口粒度熔断(每个 URL 独立计数)
      failCount: 5,           // 连续失败 5 次触发熔断
      window: 60000,          // 60 秒时间窗口(窗口外的失败不计入)
      recoverDelay: 30000,    // 熔断后 30 秒自动进入半开试探
      probeCount: 1,          // 半开状态允许 1 个试探请求通过
      statusCodes: [500, 502, 503, 504], // 只把这些 HTTP 状态码计入失败
      storage: 'memory'       // 状态存储方式:memory / session / indexedDB
    }
  }
});

按域名粒度熔断

适合某个服务整体不可用时,整个域名一起熔断:

javascript
requestManager.addRule({
  url: /\/api\/payment/,      // 支付服务
  circuitBreaker: {
    type: 'domain',           // 域名级:匹配此 URL 的所有请求共享一个熔断单元
    failCount: 3,             // 连续 3 次失败就熔断
    recoverDelay: 60000,      // 1 分钟后试探
    statusCodes: [502, 503, 504],
    storage: 'session'        // 刷新页面后状态保留(sessionStorage)
  }
});

自定义失败判定

javascript
requestManager.addRule({
  url: /\/api\/core/,
  circuitBreaker: {
    failCount: 5,
    // isFailure 优先级高于 statusCodes,配置后完全由函数决定什么算"失败"
    isFailure(error, config) {
      const status = error?.response?.status;
      return status >= 500 || error?.code === 'ECONNABORTED';
    }
  }
});

配置项

配置项类型默认值说明
enabledbooleantrue能力开关
typestring'request'熔断粒度:request(接口级)/ domain(域名级)
failCountnumber5触发熔断的连续失败次数
windownumber60000失败计数时间窗口(ms)
recoverDelaynumber30000熔断到半开的恢复延迟(ms)
probeCountnumber1半开状态允许放行的试探请求数
excludeMethodsstring[][]排除熔断的方法
statusCodesnumber[] | nullnull命中这些 HTTP 状态码才计入熔断失败;null 表示默认认所有 5xx
isFailurefunction | nullnull自定义失败判定,优先级高于 statusCodes(默认:网络错误 + 5xx)
storagestring'memory'状态存储:memory / session / indexedDB

statusCodes 只收窄有 HTTP 响应的错误;网络错误或无 status 的错误仍默认计入失败。需要完全接管失败语义时,使用 isFailure(error, config)

跨端持久化说明

session / indexedDB 只在浏览器环境真正生效。在微信/支付宝小程序、React Native、多数 SSR 运行时中,全局 sessionStorage / indexedDB 不存在,此时熔断状态会自动降级为内存存储(不跨应用重启保留),并通过你配置的 logger 上报一条 warning(含 requestedStorage / effectiveStorage)。

默认值就是 memory,因此只有显式开启持久化时才需关注。这些平台建议保持默认 memory,把熔断状态视为单次会话级。

手动控制 API

javascript
const requestManager = setupRequestGuard(axios, { /* ... */ });

// 获取某个接口的熔断状态
const state = requestManager.circuitBreaker.getState('POST:/api/payment/create');

// 获取所有熔断单元状态
const allStates = requestManager.circuitBreaker.getAllStates();

// 手动尝试恢复;满足 recoverDelay 后会切换到 HALF_OPEN
requestManager.circuitBreaker.tryRecover('POST:/api/payment/create');

// 手动重置到正常状态
requestManager.circuitBreaker.reset('POST:/api/payment/create');

错误类型

熔断拦截时抛出 RequestGuardCircuitBreakerError

javascript
try {
  await axios.post('/api/payment/create', data);
} catch (error) {
  if (error.name === 'RequestGuardCircuitBreakerError') {
    console.log(error.circuitKey);  // 熔断单元 key
    console.log(error.state);       // 当前状态:'CIRCUIT_BREAKER'
    console.log(error.failCount);   // 失败次数
    console.log(error.openedAt);    // 熔断开始时间
  }
}

类名与 error.name 不同名

导出的错误类叫 CircuitBreakerError,但实例的 error.name'RequestGuardCircuitBreakerError'(加了 RequestGuard 前缀,与 duplicate / retry 错误同源命名)。两种判别方式都可用,但不要混用

javascript
import { CircuitBreakerError } from '@hydd/request-guard';

error instanceof CircuitBreakerError              // ✅ true
error.name === 'RequestGuardCircuitBreakerError'  // ✅ true(注意是带前缀的名字)
error.name === 'CircuitBreakerError'              // ❌ false —— 不要用类名当 name 判断

推荐统一用 error.name 字符串判别:duplicate 和 retry 错误只通过 error.name 暴露、并未导出错误类,error.name 是三个能力唯一一致的识别方式。

基于 MIT 许可发布