4. 标准熔断器模板架构:模板模式在熔断器中的应用、抽象基类设计、通用接口定义

在驱动开发中,熔断器(Circuit Breaker)的实现需要兼顾状态流转的通用性与具体业务逻辑的可扩展性。模板方法模式(Template Method Pattern)天然适合这种场景:它将熔断器的骨架算法(状态检测、计数、切换)封装在抽象基类中,而将“如何判定失败”“如何执行恢复试探”等可变步骤延迟到子类实现。本节将围绕模板模式在熔断器中的落地,给出抽象基类设计、通用接口定义以及完整的代码骨架。

4.1 模板模式在熔断器中的角色映射

模板模式的核心是“不变部分由父类控制,可变部分由子类填充”。对照熔断器的标准生命周期,映射关系如下表所示:

模板模式角色 熔断器对应元素 职责说明
抽象基类(AbstractClass) CircuitBreakerTemplate 定义状态机骨架、计数逻辑、半开定时器、模板方法 call()
具体子类(ConcreteClass) NetworkBreaker / DiskBreaker 实现失败判定、健康检查、恢复策略等钩子方法
模板方法(TemplateMethod) execute_request() 固定流程:检查状态 → 执行调用 → 记录结果 → 触发状态转换
钩子方法(Hook) is_failure() / on_half_open_check() 子类可覆盖的扩展点,默认提供通用实现
设计要点:模板方法必须声明为 final(或不可重写),防止子类破坏熔断器的核心状态机;钩子方法应提供默认实现,降低子类的实现负担。

4.2 抽象基类设计:CircuitBreakerTemplate

抽象基类封装了熔断器的三态核心状态机(CLOSED / OPEN / HALF_OPEN)以及通用的计数与定时逻辑。以下是完整的抽象基类接口定义:

// circuit_breaker_template.h
#ifndef CIRCUIT_BREAKER_TEMPLATE_H
#define CIRCUIT_BREAKER_TEMPLATE_H

#include <atomic>
#include <chrono>
#include <functional>
#include <mutex>

enum class BreakerState {
    CLOSED,      // 闭合态:正常转发请求
    OPEN,        // 断开态:快速失败
    HALF_OPEN    // 半开态:试探性恢复
};

class CircuitBreakerTemplate {
public:
    CircuitBreakerTemplate(int failure_threshold,
                           int success_threshold_in_half_open,
                           std::chrono::milliseconds open_timeout)
        : failure_threshold_(failure_threshold)
        , success_threshold_in_half_open_(success_threshold_in_half_open)
        , open_timeout_(open_timeout)
        , state_(BreakerState::CLOSED)
        , failure_count_(0)
        , half_open_success_count_(0) {}

    virtual ~CircuitBreakerTemplate() = default;

    // 模板方法 —— 不可被子类重写
    bool execute_request(std::function<bool()> request) {
        std::lock_guard<std::mutex> lock(mutex_);

        if (state_ == BreakerState::OPEN) {
            if (has_open_timeout_elapsed()) {
                transition_to_half_open();
            } else {
                return false;  // 快速失败
            }
        }

        if (state_ == BreakerState::HALF_OPEN) {
            return handle_half_open_request(request);
        }

        // CLOSED 态:正常执行
        return handle_closed_request(request);
    }

    BreakerState get_state() const { return state_; }

protected:
    // ---------- 钩子方法(子类可覆盖) ----------
    // 判断一次调用是否属于失败
    virtual bool is_failure(bool result) const {
        return !result;  // 默认:返回 false 即为失败
    }

    // 半开态下的健康检查逻辑(默认直接执行请求)
    virtual bool on_half_open_check(std::function<bool()>& request) {
        return request();
    }

    // 状态转换时的通知(默认空实现)
    virtual void on_state_changed(BreakerState old_state,
                                  BreakerState new_state) {}

    // ---------- 子类可调用的辅助方法 ----------
    void reset_failure_count() { failure_count_ = 0; }
    void reset_half_open_success_count() { half_open_success_count_ = 0; }
    int get_failure_count() const { return failure_count_; }
    int get_half_open_success_count() const { return half_open_success_count_; }

private:
    // ---------- 私有核心逻辑 ----------
    bool handle_closed_request(std::function<bool()>& request) {
        bool result = request();
        if (is_failure(result)) {
            ++failure_count_;
            if (failure_count_ >= failure_threshold_) {
                transition_to_open();
            }
        } else {
            failure_count_ = 0;  // 连续成功则重置失败计数
        }
        return result;
    }

    bool handle_half_open_request(std::function<bool()>& request) {
        bool result = on_half_open_check(request);
        if (is_failure(result)) {
            transition_to_open();  // 半开态失败 → 回到断开态
            return false;
        }
        ++half_open_success_count_;
        if (half_open_success_count_ >= success_threshold_in_half_open_) {
            transition_to_closed();  // 连续成功达到阈值 → 闭合
        }
        return result;
    }

    void transition_to_open() {
        BreakerState old = state_;
        state_ = BreakerState::OPEN;
        open_time_start_ = std::chrono::steady_clock::now();
        reset_half_open_success_count();
        on_state_changed(old, BreakerState::OPEN);
    }

    void transition_to_half_open() {
        BreakerState old = state_;
        state_ = BreakerState::HALF_OPEN;
        reset_half_open_success_count();
        on_state_changed(old, BreakerState::HALF_OPEN);
    }

    void transition_to_closed() {
        BreakerState old = state_;
        state_ = BreakerState::CLOSED;
        reset_failure_count();
        reset_half_open_success_count();
        on_state_changed(old, BreakerState::CLOSED);
    }

    bool has_open_timeout_elapsed() const {
        auto now = std::chrono::steady_clock::now();
        return (now - open_time_start_) >= open_timeout_;
    }

    // ---------- 成员变量 ----------
    const int failure_threshold_;
    const int success_threshold_in_half_open_;
    const std::chrono::milliseconds open_timeout_;

    std::atomic<BreakerState> state_;
    int failure_count_;
    int half_open_success_count_;
    std::chrono::steady_clock::time_point open_time_start_;
    std::mutex mutex_;
};

#endif // CIRCUIT_BREAKER_TEMPLATE_H
架构要点:抽象基类将状态转换的判定条件(失败阈值、半开成功阈值、超时时间)作为构造函数参数注入,子类只需关注业务相关的钩子方法。所有状态转换路径(CLOSED→OPEN、OPEN→HALF_OPEN、HALF_OPEN→CLOSED/OPEN)均由基类统一管理,避免子类误操作。

4.3 通用接口定义与扩展点

除了抽象基类内部的方法,熔断器模板还需要对外暴露一组通用接口,供驱动框架或上层业务调用。这些接口遵循“最小知识原则”,只暴露必要的操作:

接口名称 类型 说明
execute_request(callable) 模板方法(public) 执行受熔断器保护的调用,自动处理状态机
get_state() 查询接口(public) 返回当前熔断器状态(CLOSED/OPEN/HALF_OPEN)
force_open() 管理接口(public) 手动强制进入断开态(用于运维或降级场景)
force_closed() 管理接口(public) 手动强制恢复闭合态(重置所有计数)
is_failure(result) 钩子方法(protected) 子类自定义失败判定逻辑
on_half_open_check(request) 钩子方法(protected) 半开态下的试探策略(如只读查询、健康端点)
on_state_changed(old, new) 钩子方法(protected) 状态变更通知(可用于日志、指标上报)
避坑提醒:force_open() 和 force_closed() 必须绕过正常的计数逻辑,直接修改状态并重置内部计数器。实现时需注意与模板方法中的锁机制保持一致,避免竞态条件。

4.4 模板架构的完整状态流转

下图展示了抽象基类中定义的完整状态流转路径,以及每个状态下的核心动作:

熔断器模板状态流转图 熔断器模板状态流转图 CLOSED 正常转发请求 OPEN 快速失败 / 等待超时 HALF_OPEN 试探性恢复 失败 ≥ 阈值 超时到达 连续成功 ≥ 阈值 试探失败 模板方法控制的关键路径: 1. CLOSED:执行请求 → 失败计数 → 达阈值则转 OPEN 2. OPEN:等待超时 → 自动转 HALF_OPEN(由模板方法触发) 3. HALF_OPEN:执行试探 → 成功计数 → 达阈值则转 CLOSED;失败则回 OPEN 子类可覆盖的扩展点(钩子方法): • is_failure() —— 自定义失败判定(如超时、错误码、异常类型) • on_half_open_check() —— 半开态试探策略(如只读查询、健康端点) • on_state_changed() —— 状态变更通知(日志、指标、告警)

4.5 具体子类实现示例

以下是一个针对网络请求熔断的具体子类实现,展示了如何覆盖钩子方法来实现业务定制的失败判定和半开试探:

// network_breaker.h
#include "circuit_breaker_template.h"
#include <string>

class NetworkBreaker : public CircuitBreakerTemplate {
public:
    NetworkBreaker(int failure_threshold,
                   int success_threshold_in_half_open,
                   std::chrono::milliseconds open_timeout)
        : CircuitBreakerTemplate(failure_threshold,
                                 success_threshold_in_half_open,
                                 open_timeout) {}

protected:
    // 覆盖失败判定:HTTP 状态码 >= 500 或网络异常视为失败
    bool is_failure(bool result) const override {
        // 实际场景中 result 可替换为更丰富的返回结构
        return !result;
    }

    // 半开态下只发送轻量级健康探测请求
    bool on_half_open_check(std::function<bool()>& request) override {
        // 记录半开试探日志
        printf("[NetworkBreaker] HALF_OPEN probe started\n");
        bool result = request();  // 实际可替换为 /health 端点
        printf("[NetworkBreaker] HALF_OPEN probe result: %s\n",
               result ? "success" : "failure");
        return result;
    }

    // 状态变更时输出日志
    void on_state_changed(BreakerState old_state,
                          BreakerState new_state) override {
        printf("[NetworkBreaker] State: %s → %s\n",
               state_to_string(old_state).c_str(),
               state_to_string(new_state).c_str());
    }

private:
    std::string state_to_string(BreakerState s) const {
        switch (s) {
            case BreakerState::CLOSED:   return "CLOSED";
            case BreakerState::OPEN:     return "OPEN";
            case BreakerState::HALF_OPEN: return "HALF_OPEN";
            default: return "UNKNOWN";
        }
    }
};
实践建议:在驱动开发中,建议将熔断器模板作为组合对象嵌入到设备驱动类中,而非使用继承。例如,一个网卡驱动可以包含一个 NetworkBreaker 成员,在发送数据包时调用 execute_request()。这样既复用了模板的状态机,又保持了驱动类的单一职责。

4.6 模板架构的优劣分析

维度 优势 潜在风险
代码复用 状态机、计数、定时逻辑全部集中在基类,子类只需写业务差异代码 基类过于臃肿时,子类可能被迫依赖不需要的成员
扩展性 钩子方法提供清晰的扩展点,新增熔断策略无需修改基类 钩子方法过多会导致“模板方法反模式”,子类需覆盖大量方法
可测试性 基类状态机可独立测试,子类只需测试钩子方法 模板方法中的锁和原子操作增加并发测试复杂度
维护性 状态转换路径集中管理,避免子类各自实现导致的状态不一致 基类与子类之间的耦合较紧,修改基类可能影响所有子类

综合来看,模板模式在熔断器场景中的收益远大于风险。只要控制好基类的职责边界(只做状态机骨架,不做业务假设),并合理设计钩子方法的数量(通常 3~5 个),就能在驱动开发中构建出高内聚、低耦合的标准熔断器模板。