断路器模式 Circuit Breaker Pattern
描述
    在服务提供方不可用时保护服务调用方的资源,减少不必要的远程调用。使用该模式可以保护服务调用方的稳定性,防止资源浪费,提高服务可用性。背景和问题
    在分布式系统中,一个请求可能会经过多个服务,虽然这种把请求压力打散到不同服务的方式大大提高了请求的性能,
    但是当其中一个服务不可用时,可能会导致整个请求都不可用,严重的情况下甚至会影响到整个分布式系统,从而导致雪崩的情况。解决方案
    使用一个断路器对象监控受保护的服务,当服务的故障次数达到一定的阀值,断路器会跳闸,跳闸后,所有后继调用将不会发送到受保护的服务,而是直接由断路器返回错误。熔断设计的目的是在服务提供方不可用时保护服务调用方的资源,减少服务调用中无用的远程调用。注意事项
- 要做好异常处理。
- 不同的异常来配置不同策略。
- 记录异常的日志记录。
- 断路器的恢复策略。
- 对失败的操作进行测试。
- 调用重试。
- 并发性。同一个断路器有可能同时被大量的请求使用,在设计和实现断路器时要注意。
何时使用
一个服务依赖另一个远程服务,并且在某些情况下很可能失败时。结构中包含的角色
- CircuitBreakerEventCode 断路器事件常量类
- CircuitBreakerStateCode 断路器状态常量类
- Event 事件
- AbstractTransition 抽象动作
- SkipTransition 跳闸动作
- RecoverTransition 恢复动作
- CircuitBreakerStateMachine 断路器状态机
- Service 抽象服务
- ProtectedSerivce 受保护的服务
- 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(); 
           99+种软件模式
99+种软件模式 
         
                     
                     
             
             关于 LearnKu
                关于 LearnKu
               
                     
                     
                     粤公网安备 44030502004330号
 粤公网安备 44030502004330号 
 
推荐文章: