熔断器
熔断器模式配置参考和模拟,理解Circuit Breaker的工作原理
🔧 配置生成
🎮 模拟器
📖 设计模式
💻 实现代码
CLOSED
正常
正常
失败率超阈值 →
OPEN
熔断
熔断
超时后 →
HALF-OPEN
半开
半开
超过此阈值触发熔断
统计窗口内的请求数
OPEN状态持续时间
HALF-OPEN状态允许的试探请求数
超过此时间视为慢调用
慢调用占比超此值触发熔断
熔断器三状态
CLOSED (关闭/正常) → 请求正常通过 → 统计失败率 → 失败率超阈值 → 切换到 OPEN OPEN (打开/熔断) → 请求直接拒绝(快速失败) → 返回降级响应或错误 → 超时后 → 切换到 HALF-OPEN HALF-OPEN (半开) → 允许少量请求通过(试探) → 试探成功 → 切换到 CLOSED → 试探失败 → 切换回 OPEN
降级策略
1. 返回缓存数据 → 返回上次成功的缓存结果 2. 返回默认值 → 返回安全的默认响应 3. 返回精简数据 → 返回核心功能,跳过增强功能 4. 队列化请求 → 将请求放入队列,稍后重试 5. 快速失败 → 直接返回错误,让调用方处理
与其他模式配合
熔断器 + 重试 → 重试在熔断器之前 → 重试失败后才计入熔断器统计 熔断器 + 限流 → 限流在熔断器之前 → 限流拒绝不计入熔断器统计 熔断器 + 舱壁 → 每个下游服务独立熔断器 → 一个服务熔断不影响其他服务
Node.js 实现
class CircuitBreaker {
constructor(options = {}) {
this.failureThreshold = options.failureThreshold || 50;
this.windowSize = options.windowSize || 10;
this.openDuration = options.openDuration || 30000;
this.halfOpenRequests = options.halfOpenRequests || 3;
this.state = 'CLOSED';
this.failures = 0;
this.successes = 0;
this.openedAt = null;
this.halfOpenCount = 0;
}
async execute(fn, fallback) {
if (this.state === 'OPEN') {
if (Date.now() - this.openedAt > this.openDuration) {
this.state = 'HALF-OPEN';
this.halfOpenCount = 0;
} else {
return fallback ? fallback() :
Promise.reject(new Error('Circuit is OPEN'));
}
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
return fallback ? fallback(error) : Promise.reject(error);
}
}
onSuccess() {
if (this.state === 'HALF-OPEN') {
this.halfOpenCount++;
if (this.halfOpenCount >= this.halfOpenRequests) {
this.state = 'CLOSED';
this.failures = 0;
this.successes = 0;
}
}
this.successes++;
this.failures = 0;
}
onFailure() {
this.failures++;
if (this.state === 'HALF-OPEN') {
this.state = 'OPEN';
this.openedAt = Date.now();
return;
}
const total = this.failures + this.successes;
if (total >= this.windowSize) {
const rate = (this.failures / total) * 100;
if (rate >= this.failureThreshold) {
this.state = 'OPEN';
this.openedAt = Date.now();
}
}
}
}
// 使用
const breaker = new CircuitBreaker({
failureThreshold: 50,
windowSize: 10,
openDuration: 30000,
});
const result = await breaker.execute(
() => fetch('/api/data').then(r => r.json()),
() => ({ data: 'cached-fallback' })
);Resilience4j (Java)
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.slidingWindowSize(10)
.waitDurationInOpenState(Duration.ofSeconds(30))
.permittedNumberOfCallsInHalfOpenState(3)
.slowCallDurationThreshold(Duration.ofSeconds(3))
.slowCallRateThreshold(80)
.build();
CircuitBreaker breaker = CircuitBreaker.of("api", config);
Supplier<String> supplier = CircuitBreaker
.decorateSupplier(breaker, () -> apiClient.getData());
String result = Try.ofSupplier(supplier)
.recover(throwable -> "fallback").get();Polly (.NET)
var breaker = Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (ex, breakDelay) =>
Console.WriteLine($"Circuit OPEN for {breakDelay.TotalSeconds}s"),
onReset: () => Console.WriteLine("Circuit CLOSED")
);
var result = await breaker.ExecuteAsync(async () => {
var response = await httpClient.GetAsync("/api/data");
response.EnsureSuccessStatusCode();
return response;
});