断路器模式 Circuit Breaker Pattern

未匹配的标注

描述

    在服务提供方不可用时保护服务调用方的资源,减少不必要的远程调用。使用该模式可以保护服务调用方的稳定性,防止资源浪费,提高服务可用性。

背景和问题

    在分布式系统中,一个请求可能会经过多个服务,虽然这种把请求压力打散到不同服务的方式大大提高了请求的性能,
    但是当其中一个服务不可用时,可能会导致整个请求都不可用,严重的情况下甚至会影响到整个分布式系统,从而导致雪崩的情况。

解决方案

    使用一个断路器对象监控受保护的服务,当服务的故障次数达到一定的阀值,断路器会跳闸,跳闸后,所有后继调用将不会发送到受保护的服务,而是直接由断路器返回错误。熔断设计的目的是在服务提供方不可用时保护服务调用方的资源,减少服务调用中无用的远程调用。

注意事项

  1. 要做好异常处理。
  2. 不同的异常来配置不同策略。
  3. 记录异常的日志记录。
  4. 断路器的恢复策略。
  5. 对失败的操作进行测试。
  6. 调用重试。
  7. 并发性。同一个断路器有可能同时被大量的请求使用,在设计和实现断路器时要注意。

何时使用

一个服务依赖另一个远程服务,并且在某些情况下很可能失败时。

结构中包含的角色

  1. CircuitBreakerEventCode 断路器事件常量类
  2. CircuitBreakerStateCode 断路器状态常量类
  3. Event 事件
  4. AbstractTransition 抽象动作
  5. SkipTransition 跳闸动作
  6. RecoverTransition 恢复动作
  7. CircuitBreakerStateMachine 断路器状态机
  8. Service 抽象服务
  9. ProtectedSerivce 受保护的服务
  10. CircuitBreaker 断路器

可用到的设计模式思维

断路器监控所有请求,这里可以使用代理模式处理。
断路器内部的状态(正常,跳闸)切换,可以使用状态模式处理。

最小可表达代码

// 断路器事件常量类
class CircuitBreakerEventCode {
    const SKIP = "跳闸";
    const RECOVER = "恢复";
}

// 断路器状态常量类
class CircuitBreakerStateCode {
    const OPEN = "打开";
    const CLOSED = "关闭";
}

// 事件
class Event
{
    private $eventCode;

    public function __construct(String $eventCode)
    {
        $this->eventCode = $eventCode;
    }

    public function getEventCode()
    {
        return $this->eventCode;
    }
}

// 抽象动作
abstract class AbstractTransition
{
    protected $currentState; // 现态
    protected $nextState; // 次态

    public function __construct(String $currentState, String $nextState)
    {
        $this->currentState = $currentState;
        $this->nextState = $nextState;
    }

    // 具体动作需要执行的方法
    protected abstract function subHandle(Event $event) : bool;

    // 定义动作执行的方法
    public function handle(Event $event)
    {
        if ($this->subHandle($event)) {
            return $this->nextState;
        }

        echo 'throw new Exception 抛错';
    }
}

// 跳闸动作
class SkipTransition extends AbstractTransition
{
    protected function subHandle(Event $event) : bool
    {
        var_dump("我是跳闸 状态从{$this->currentState}切换成{$this->nextState}");
        return true;
    }
}

// 恢复动作
class RecoverTransition extends AbstractTransition
{
    protected function subHandle(Event $event) : bool
    {
        var_dump("我是恢复 状态从{$this->currentState}切换成{$this->nextState}");
        return true;
    }
}

// 断路器状态机
class CircuitBreakerStateMachine
{
    protected $currentState = CircuitBreakerStateCode::OPEN;

    public function isAvailable() : bool
    {
        return CircuitBreakerStateCode::OPEN == $this->currentState;
    }

    public function skip()
    {
        $this->currentState = $this->handle(new Event(CircuitBreakerEventCode::SKIP));
    }

    public function recover()
    {
        $this->currentState = $this->handle(new Event(CircuitBreakerEventCode::RECOVER));
    }

    public function getStateEventTransitionMap() : array
    {
        return [
            CircuitBreakerEventCode::SKIP => new SkipTransition(CircuitBreakerStateCode::OPEN, CircuitBreakerStateCode::CLOSED),
            CircuitBreakerEventCode::RECOVER => new SkipTransition(CircuitBreakerStateCode::CLOSED, CircuitBreakerStateCode::OPEN),
        ];
    }

    protected function getTransition(String $eventCode)
    {
        $stateEventTransitionMap = $this->getStateEventTransitionMap();

        return $stateEventTransitionMap[$eventCode] ?? [];
    }

    public  function handle(Event $event)
    {
        $eventCode = $event->getEventCode();

        if ($transition = $this->getTransition($eventCode)) {
            return $transition->handle($event);
        }

        echo 'throw new Exception 抛错';
    }
}


// 抽象服务
interface Service {
    public function request();
}

// 受保护的服务
class ProtectedSerivce implements Service 
{
    public function request()
    {
        var_dump("测试请求受保护服务失败...");
        throw new Exception("测试");
    }
}

// 断路器
class CircuitBreaker implements Service
{
    private $service;
    private $stateMachine;
    private $errorCounter = 0;
    private $maxErrorCount = 2;

    public function __construct()
    {  
        $this->service = new ProtectedSerivce();
        $this->stateMachine = new CircuitBreakerStateMachine();
    }

    public function request() 
    {
        if ($this->stateMachine->isAvailable()) {
            try {
                $this->service->request();
            } catch (Exception $e) {
                $this->errorCounter++;
            } finally {
                if ($this->errorCounter >= $this->maxErrorCount) {
                    $this->stateMachine->skip();
                }
            }
        }

        if (! $this->stateMachine->isAvailable()) {
            var_dump("服务不可用...");
        }
    }
}

$circuitBreaker = new CircuitBreaker();
$circuitBreaker->request();
$circuitBreaker->request();
$circuitBreaker->request();

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~