能力:熔断守护(Circuit Breaker)
接口持续失败时自动熔断,避免无意义请求打爆服务端,支持半开试探自动恢复。
工作原理
正常状态 (NORMAL) → 连续失败达到阈值 → 熔断状态 (CIRCUIT_BREAKER)
↓ 等待 recoverDelay
半开状态 (HALF_OPEN)
↓ 试探请求成功
正常状态 (NORMAL)默认仅网络错误和 5xx 计入失败;ERR_CANCELED、AbortError、ABORT_ERR 和 Axios legacy cancel 不计失败,半开取消也不会被判定为恢复成功。
单业务场景配置
全局开启熔断保护
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
}
}
});按域名粒度熔断
适合某个服务整体不可用时,整个域名一起熔断:
requestManager.addRule({
url: /\/api\/payment/, // 支付服务
circuitBreaker: {
type: 'domain', // 域名级:匹配此 URL 的所有请求共享一个熔断单元
failCount: 3, // 连续 3 次失败就熔断
recoverDelay: 60000, // 1 分钟后试探
statusCodes: [502, 503, 504],
storage: 'session' // 刷新页面后状态保留(sessionStorage)
}
});自定义失败判定
requestManager.addRule({
url: /\/api\/core/,
circuitBreaker: {
failCount: 5,
// isFailure 优先级高于 statusCodes,配置后完全由函数决定什么算"失败"
isFailure(error, config) {
const status = error?.response?.status;
return status >= 500 || error?.code === 'ECONNABORTED';
}
}
});配置项
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | boolean | true | 能力开关 |
type | string | 'request' | 熔断粒度:request(接口级)/ domain(域名级) |
failCount | number | 5 | 触发熔断的连续失败次数 |
window | number | 60000 | 失败计数时间窗口(ms) |
recoverDelay | number | 30000 | 熔断到半开的恢复延迟(ms) |
probeCount | number | 1 | 半开状态允许放行的试探请求数 |
excludeMethods | string[] | [] | 排除熔断的方法 |
statusCodes | number[] | null | null | 命中这些 HTTP 状态码才计入熔断失败;null 表示默认认所有 5xx |
isFailure | function | null | null | 自定义失败判定,优先级高于 statusCodes(默认:网络错误 + 5xx) |
storage | string | 'memory' | 状态存储:memory / session / indexedDB |
statusCodes 只收窄有 HTTP 响应的错误;网络错误或无 status 的错误仍默认计入失败。需要完全接管失败语义时,使用 isFailure(error, config)。
跨端持久化说明
session / indexedDB 只在浏览器环境真正生效。在微信/支付宝小程序、React Native、多数 SSR 运行时中,全局 sessionStorage / indexedDB 不存在,此时熔断状态会自动降级为内存存储(不跨应用重启保留),并通过你配置的 logger 上报一条 warning(含 requestedStorage / effectiveStorage)。
默认值就是 memory,因此只有显式开启持久化时才需关注。这些平台建议保持默认 memory,把熔断状态视为单次会话级。
手动控制 API
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:
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 错误同源命名)。两种判别方式都可用,但不要混用:
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 是三个能力唯一一致的识别方式。
