断路器模式 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 网站上。

上一篇 下一篇
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~