为什么快速失败有帮助
当依赖挂起时,调用方等待超时,线程或连接池被占满,故障扩散到健康路径。熔断器把反复的慢失败转换为本地快速失败,调用方可以降级、返回缓存或清晰错误,而不再消耗更多资源。
状态机
type State = 'closed' | 'open' | 'half-open';
class CircuitBreaker {
private state: State = 'closed';
private failures = 0;
private openedAt = 0;
constructor(
private readonly threshold = 5,
private readonly cooldownMs = 30_000,
) {}
async call<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === 'open') {
if (Date.now() - this.openedAt < this.cooldownMs) {
throw new Error('circuit open');
}
this.state = 'half-open';
}
try {
const result = await operation();
this.failures = 0;
this.state = 'closed';
return result;
} catch (error) {
this.failures += 1;
if (this.state === 'half-open' || this.failures >= this.threshold) {
this.state = 'open';
this.openedAt = Date.now();
}
throw error;
}
}
}
选择阈值
阈值应反映依赖的正常错误率和调用方的容忍度。只统计基础设施失败,不统计校验错误。使用滚动窗口或连续失败计数,并让冷却时间足够依赖恢复,又足够短以检测恢复。
何时使用
在具有部分失败模式的远程依赖上使用熔断器:支付提供商、搜索服务、遗留 API。与重试策略相比,熔断器在故障期间减少负载,而重试增加负载;应组合使用,使重试只发生在熔断器关闭时。与限流相比,熔断器对失败作出反应,而不是对流量大小。
陷阱
永久打开的熔断器会阻断所有流量;必须定义半开探测。每个主机一个熔断器可能掩盖区域故障;每个依赖一个熔断器可能因单个坏实例过度跳闸。应记录状态转换和失败原因,帮助运维区分真实故障与阈值配置错误。