熔断基础概念:熔断器三态及状态转换机制
熔断器(Circuit Breaker)是分布式系统中防止级联故障的关键模式。在模型推理服务中,当上游请求持续超时或下游模型服务返回错误时,熔断器可以快速切断调用链路,给系统留出恢复时间。熔断器的核心在于其三态模型:关闭(Closed)、打开(Open)和半开(Half-Open),以及三者之间的转换逻辑。
熔断器三态定义
| 状态 | 含义 | 行为表现 | 典型触发条件 |
|---|---|---|---|
| 关闭(Closed) | 正常状态,请求直接通过 | 请求正常转发到模型服务,熔断器统计失败次数 | 系统初始化或故障恢复后 |
| 打开(Open) | 故障状态,请求被快速拒绝 | 直接返回错误或降级响应,不调用下游服务 | 失败次数超过阈值(如连续5次超时) |
| 半开(Half-Open) | 试探状态,允许少量请求通过 | 放行一个或多个探测请求,检查下游是否恢复 | 打开状态持续超时时间(如30秒)后自动进入 |
状态转换机制
熔断器的状态转换遵循一个闭环流程,从关闭开始,经过打开、半开,最终回到关闭或重新打开。下图展示了完整的转换路径:
状态转换的详细规则
- 关闭 → 打开:熔断器在关闭状态下持续统计请求失败次数(超时、HTTP 5xx、网络异常等)。当失败次数在指定时间窗口内达到阈值(例如 10 秒内连续 5 次失败),状态立即切换为打开。
- 打开 → 半开:熔断器在打开状态下等待一个固定的超时时间(例如 30 秒)。超时时间到达后,自动进入半开状态,准备发送探测请求。
- 半开 → 关闭:在半开状态下,熔断器放行一个或多个探测请求。如果探测请求成功(例如模型推理返回正常结果),则认为下游服务已恢复,状态切换回关闭。
- 半开 → 打开:如果半开状态下的探测请求仍然失败,熔断器认为故障尚未解除,立即回到打开状态,并重置超时计时器。
关键参数说明
| 参数名称 | 典型值 | 作用 |
|---|---|---|
| 失败阈值(failureThreshold) | 5 | 关闭状态下连续失败次数达到该值后触发打开 |
| 超时时间(timeout) | 30 秒 | 打开状态持续该时间后自动进入半开 |
| 探测请求数(maxRetries) | 1 | 半开状态下允许通过的探测请求数量 |
| 统计窗口(windowSize) | 10 秒 | 失败计数的时间窗口,窗口过期后计数重置 |
实践建议:在模型推理场景中,建议将失败阈值设置为 3-5 次,超时时间设置为 30-60 秒。过短的超时时间会导致熔断器频繁切换,增加系统抖动;过长的超时时间则无法及时保护下游服务。
状态转换的代码示例
以下是一个简化的熔断器状态机实现,展示了三态转换的核心逻辑:
class CircuitBreaker:
def __init__(self, failure_threshold=5, timeout=30, max_retries=1):
self.state = "CLOSED"
self.failure_count = 0
self.failure_threshold = failure_threshold
self.timeout = timeout
self.max_retries = max_retries
self.last_failure_time = None
def call(self, func):
if self.state == "OPEN":
if self._timeout_expired():
self.state = "HALF_OPEN"
else:
raise Exception("Circuit breaker is OPEN")
try:
result = func()
self._on_success()
return result
except Exception as e:
self._on_failure()
raise e
def _on_success(self):
if self.state == "HALF_OPEN":
self.state = "CLOSED"
self.failure_count = 0
elif self.state == "CLOSED":
self.failure_count = 0
def _on_failure(self):
if self.state == "HALF_OPEN":
self.state = "OPEN"
self.last_failure_time = time.time()
elif self.state == "CLOSED":
self.failure_count += 1
if self.failure_count >= self.failure_threshold:
self.state = "OPEN"
self.last_failure_time = time.time()
def _timeout_expired(self):
if self.last_failure_time is None:
return True
return time.time() - self.last_failure_time > self.timeout
注意:生产环境中不要直接使用上述简化代码。建议使用成熟的熔断器库(如
pybreaker、hystrix-py 或 resilience4j),它们提供了更完善的线程安全、统计窗口和指标上报功能。
状态转换的时序图
下面的时序图展示了熔断器在正常请求、故障触发、超时恢复和再次故障的完整生命周期:
常见问题与避坑
- 状态抖动:如果超时时间设置过短,熔断器会在打开和半开之间频繁切换,导致系统不稳定。建议超时时间至少为平均恢复时间的 2-3 倍。
- 探测请求数量:半开状态下不要放行过多探测请求,否则可能瞬间压垮正在恢复的下游服务。通常设置为 1 个请求即可。
- 统计窗口重置:关闭状态下的失败计数应在时间窗口过期后自动重置,避免历史失败累积导致误触发。
- 与限流配合:熔断器应与限流器配合使用。限流控制请求速率,熔断控制请求通路,两者互补才能构建完整的防护体系。