断路器模式 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();
推荐文章: