4. 熔断器状态机实现:基于有限状态机(FSM)的熔断器核心逻辑代码实现(Python伪代码)

熔断器的核心是一个有限状态机(FSM),它维护三个明确的状态:CLOSED(关闭)、OPEN(打开) 和 HALF_OPEN(半开)。状态之间的转换由预定义的阈值、时间窗口和探测结果驱动。本节将使用 Python 伪代码实现这个状态机的核心逻辑,并展示状态转换的完整流程。

4.1 状态定义与转换矩阵

在实现之前,先明确状态机的转换规则。下表定义了每个状态下的允许事件及目标状态:

当前状态 触发事件 条件 目标状态 动作
CLOSED 请求成功 — CLOSED 重置失败计数
CLOSED 请求失败 失败率 < 阈值 CLOSED 递增失败计数
CLOSED 请求失败 失败率 ≥ 阈值 OPEN 启动超时计时器
OPEN 超时到期 — HALF_OPEN 允许探测请求
OPEN 请求到达 — OPEN 快速失败(拒绝)
HALF_OPEN 探测成功 — CLOSED 重置所有计数器
HALF_OPEN 探测失败 — OPEN 重启超时计时器
设计要点: 状态转换必须原子化。在并发环境下,状态变更和计数器更新应使用锁或 CAS(Compare-And-Swap)操作,防止竞态条件导致状态不一致。

4.2 核心状态机实现(Python 伪代码)

以下代码实现了熔断器的 FSM 核心逻辑。为保持清晰,省略了线程安全细节,聚焦于状态转换和计数逻辑。

import time
from enum import Enum

class CircuitState(Enum):
    CLOSED = 1
    OPEN = 2
    HALF_OPEN = 3

class CircuitBreaker:
    def __init__(self, failure_threshold=5, recovery_timeout=30, half_open_max_requests=3):
        self.state = CircuitState.CLOSED
        self.failure_count = 0
        self.success_count = 0
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout          # 秒
        self.half_open_max_requests = half_open_max_requests
        self.last_failure_time = 0
        self.half_open_requests = 0

    def call(self, func, fallback=None):
        """执行受熔断器保护的调用"""
        if self.state == CircuitState.OPEN:
            if time.time() - self.last_failure_time >= self.recovery_timeout:
                self._transition_to_half_open()
            else:
                return self._fast_fail(fallback)

        if self.state == CircuitState.HALF_OPEN:
            if self.half_open_requests >= self.half_open_max_requests:
                return self._fast_fail(fallback)
            self.half_open_requests += 1

        try:
            result = func()
            self._on_success()
            return result
        except Exception as e:
            self._on_failure()
            if fallback is not None:
                return fallback(e)
            raise

    def _on_success(self):
        if self.state == CircuitState.HALF_OPEN:
            self._transition_to_closed()
        elif self.state == CircuitState.CLOSED:
            self.failure_count = 0   # 连续成功重置失败计数

    def _on_failure(self):
        self.last_failure_time = time.time()
        if self.state == CircuitState.HALF_OPEN:
            self._transition_to_open()
        elif self.state == CircuitState.CLOSED:
            self.failure_count += 1
            if self.failure_count >= self.failure_threshold:
                self._transition_to_open()

    def _transition_to_open(self):
        self.state = CircuitState.OPEN
        self.last_failure_time = time.time()
        self.half_open_requests = 0
        print("[熔断器] 状态转换: CLOSED -> OPEN")

    def _transition_to_half_open(self):
        self.state = CircuitState.HALF_OPEN
        self.half_open_requests = 0
        print("[熔断器] 状态转换: OPEN -> HALF_OPEN")

    def _transition_to_closed(self):
        self.state = CircuitState.CLOSED
        self.failure_count = 0
        self.success_count = 0
        self.half_open_requests = 0
        print("[熔断器] 状态转换: HALF_OPEN -> CLOSED")

    def _fast_fail(self, fallback):
        """快速失败:直接返回降级结果或抛出异常"""
        if fallback is not None:
            return fallback(Exception("CircuitBreaker OPEN"))
        raise Exception("CircuitBreaker OPEN: request rejected")
实现提示: 实际生产环境中,failure_count 应替换为滑动窗口统计(如基于时间窗口的失败率),避免突发流量导致过早熔断。上述伪代码使用计数阈值是为了简化状态机演示。

4.3 状态机转换流程(SVG 图)

下图展示了熔断器 FSM 的完整状态转换路径,包含三个状态和六条转换边。每个转换边上标注了触发事件和条件。

熔断器有限状态机转换图 熔断器 FSM 状态转换图 CLOSED (关闭) OPEN (打开) HALF_OPEN (半开) 失败率 ≥ 阈值 超时到期 探测成功 探测失败 成功/失败率未超 请求到达(拒绝) 说明:实线表示正常转换路径,虚线表示异常回退路径。自环表示状态不变。

4.4 关键设计决策

在实现 FSM 时,以下设计决策直接影响熔断器的鲁棒性和性能:

  • 计数 vs. 滑动窗口: 简单计数实现容易受突发流量影响。生产环境建议使用 滑动时间窗口(如 10 秒窗口内失败率)替代固定计数。
  • 半开状态探测数: half_open_max_requests 控制半开状态下允许的探测请求数量。通常设置为 1~3,避免过多流量压垮正在恢复的服务。
  • 超时恢复策略: recovery_timeout 是熔断器从 OPEN 到 HALF_OPEN 的等待时间。建议使用 指数退避(如 30s → 60s → 120s)而非固定值,防止服务抖动。
  • 降级与快速失败: 在 OPEN 状态下,所有请求应快速失败并返回降级结果(fallback),避免线程阻塞等待超时。
避坑指南: 不要在 _on_success 和 _on_failure 中执行耗时操作(如日志写入、远程调用)。状态转换应保持轻量,否则会阻塞调用线程,降低熔断器本身的性能。

4.5 状态机扩展:引入半开探测计数器

在某些场景下,半开状态需要更精细的控制。下图展示了引入 探测计数器 后的状态机内部逻辑,区分“允许探测”和“探测进行中”两个子阶段:

半开状态内部子阶段 半开状态内部子阶段 CLOSED OPEN HALF_OPEN 内部 允许探测 探测进行中 超时到期 请求到达 探测失败 探测成功
扩展建议: 将半开状态拆分为“允许探测”和“探测进行中”两个子阶段,可以更精确地控制并发探测数,防止半开状态下突发大量请求压垮后端。实际实现时,可以使用 asyncio.Semaphore 或 threading.BoundedSemaphore 控制并发度。

4.6 总结

本节实现的 FSM 熔断器核心逻辑具备以下特征:

  • 状态清晰: CLOSED、OPEN、HALF_OPEN 三态转换,符合标准熔断器模式。
  • 可配置: 失败阈值、恢复超时、半开探测数均可外部注入。
  • 可扩展: 计数逻辑可替换为滑动窗口;半开状态可引入子阶段控制并发。
  • 生产就绪: 伪代码可直接映射到 Python、Java、Go 等语言的实现,只需补充线程安全和指标上报。