最近在作分布式服务熔断,由于要实现一个熔断器状态机,因此想到状态模式。状态模式是当一个对象的内在状态改变时容许改变其行为,这个对象看起来像是改变了其类。状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的状况。把状态的判断逻辑转移到表示不一样状态的一系列类中,能够把复杂的判断逻辑简化。html
先举个简单的例子,以红绿灯模型,说明状态模式是怎么一回事:java
一般状况下咱们是这样实现的:并发
public class TrafficLight { private static enum State { RED, GREEN, YELLOW } private static State state = State.RED; public static void change() { switch (state) { case RED: System.out.println("--------\n红灯(5s)"); sleep(5000); state = State.GREEN; break; case GREEN: System.out.println("绿灯(5s)"); sleep(5000); state = State.YELLOW; break; case YELLOW: System.out.println("黄灯(2s)"); sleep(2000); state = State.RED; } } private static void sleep(int second) { try { Thread.sleep(second); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { while (true) { TrafficLight.change(); } } }
输出:分布式
以上这种写法,当增长一种灯控逻辑的时候就须要再添加一个分支,致使咱们须要不断的去修改代码逻辑,下面使用状态模式实现:ide
定义状态接口测试
public interface LightState { void showLight(LightManager manager, LightState nextState); }
咱们但愿显示灯亮的时候程序休眠几秒,但咱们不但愿在LightState的每一个实现类里面去定义一个sleep方法,因此咱们定义一个抽象类Light来实现上面的接口ui
public abstract class Light implements LightState { protected void sleep(int second) { try { Thread.sleep(second); } catch (InterruptedException e) { e.printStackTrace(); } } }
而后下面分别写LightState接口的三个实现类this
public class RedLight extends Light { @Override public void showLight(LightManager manager, LightState nextState) { System.out.println("---------\n红灯!(5s)"); sleep(5000); manager.setLightState(nextState); } }
public class GreenLight extends Light { @Override public void showLight(LightManager manager, LightState nextState) { System.out.println("绿灯!(5s)"); sleep(5000); manager.setLightState(nextState); } }
public class YellowLight extends Light { @Override public void showLight(LightManager manager, LightState nextState) { System.out.println("黄灯!(2s)"); sleep(2000); manager.setLightState(nextState); } }
接下来咱们要实现一个灯控的管理类spa
public class LightManager { private LightState lightState; public LightManager(LightState lightState) { this.lightState = lightState; } public void setLightState(LightState lightState) { this.lightState = lightState; } public void changeLight(LightState nextState) { lightState.showLight(this, nextState); } }
测试类3d
public class Test { public static void main(String[] args) { LightState[] states = {new RedLight(), new GreenLight(), new YellowLight()}; int index = 0; LightManager manager = new LightManager(states[index++]); while (true) { manager.changeLight(states[index++]); if (index == states.length) index = 0; } } }
输出结果:
这样若是后面咱们须要新增一个灯控颜色的好比蓝色的话,只须要实现LightState的实现类接口,不用修改到代码逻辑。
----------------------------------------------熔断器实现------------------------------------------------
接下来用状态模式实现下熔断器
整个状态机的逻辑大体以下:
服务熔断主要是参考电路熔断,若是一条线路电压太高,保险丝会熔断,防止火灾。放到咱们的系统中,若是某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。若是目标服务状况好转则恢复调用。熔断器可使用状态机来实现,内部模拟如下几种状态。
熔断器核心功能其实是维护一个状态机,并定义好状态转移的规则,这里定义一个状态转移操做的抽象类AbstractBreakerState,状态机的抽象类图以下:
/** * 熔断器状态转移操做的抽象类 */ public abstract class AbstractBreakerState { protected BreakerManager manager; public AbstractBreakerState(BreakerManager manager) { this.manager = manager; } /** * 调用方法以前处理的操做 */ public void protectedCodeIsAboutToBeCalled() { //若是是断开状态,直接返回,而后等超时转换到半断开状态 if (manager.isOpen()) { throw new RuntimeException("服务已熔断,请稍等重试!"); } } /** * 方法调用成功以后的操做 */ public void protectedCodeHasBeenCalled() { manager.increaseSuccessCount(); } /** * 方法调用发生异常操做后的操做 */ public void ActUponException() { //增长失败次数计数器,而且保存错误信息 manager.increaseFailureCount(); //重置连续成功次数 manager.resetConsecutiveSuccessCount(); } }
/** * 熔断器闭合状态 * 在闭合状态下,若是发生错误,而且错误次数达到阈值,则状态机切换到断开状态 */ public class ClosedState extends AbstractBreakerState { public ClosedState(BreakerManager manager) { super(manager); //重置失败计数器 manager.resetFailureCount(); } @Override public void ActUponException() { super.ActUponException(); //若是失败次数达到阈值,则切换到断开状态 if (manager.failureThresholdReached()) { manager.moveToOpenState(); } } }
/** * 熔断器断开状态 * 断开状态内部维护一个计数器,若是断开达到必定的时间,则自动切换到半断开状态,而且,在断开状态下,若是须要执行操做,则直接抛出异常。 */ public class OpenState extends AbstractBreakerState { public OpenState(BreakerManager manager) { super(manager); final Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { timeoutHasBeenReached(); timer.cancel(); } }, manager.timeout); } @Override public void protectedCodeIsAboutToBeCalled() { super.protectedCodeIsAboutToBeCalled(); throw new RuntimeException("服务已熔断,请稍等重试!"); } /** * 断开超过设定的阈值,自动切换到半断开状态 */ private void timeoutHasBeenReached() { manager.moveToHalfOpenState(); } }
/** * 熔断器半断开状态 * 切换到半断开状态时,将连续成功调用计数重置为0,当执行成功的时候,自增该字段,当达到连读调用成功次数的阈值时,切换到闭合状态。 * 若是调用失败,当即切换到断开模式。 */ public class HalfOpenState extends AbstractBreakerState { public HalfOpenState(BreakerManager manager) { super(manager); //重置连续成功计数 manager.resetConsecutiveSuccessCount(); } @Override public void ActUponException() { super.ActUponException(); //只要有失败,当即切换到断开模式 manager.moveToOpenState(); } @Override public void protectedCodeHasBeenCalled() { super.protectedCodeHasBeenCalled(); //若是连续成功次数达到阈值,切换到闭合状态 if (manager.consecutiveSuccessThresholdReached()) { manager.moveToClosedState(); } } }
状态管理器:
/** * 熔断器管理类 */ public class BreakerManager { public int failureCount; //失败次数 public int consecutiveSuccessCount; //连续成功次数 public int failureThreshold; //最大调用失败次数 public int consecutiveSuccessThreshold; //连续调用成功次数 public int timeout; private AbstractBreakerState state; //当前熔断器状态 public boolean isClosed() { return state instanceof ClosedState; } public boolean isOpen() { return state instanceof OpenState; } public boolean isHalfOpen() { return state instanceof HalfOpenState; } protected void moveToClosedState() { state = new ClosedState(this); } protected void moveToOpenState() { state = new OpenState(this); } protected void moveToHalfOpenState() { state = new HalfOpenState(this); } protected void increaseFailureCount() { failureCount++; } public void resetFailureCount() { failureCount = 0; } protected boolean failureThresholdReached() { return failureCount >= failureThreshold; } protected void increaseSuccessCount() { consecutiveSuccessCount++; } protected void resetConsecutiveSuccessCount() { consecutiveSuccessCount = 0; } protected boolean consecutiveSuccessThresholdReached() { return consecutiveSuccessCount >= consecutiveSuccessThreshold; } /** * Close状态下最大失败次数,HalfOpen状态下使用的最大连续成功次数,以及Open状态下的超时时间 * 在初始状态下,熔断器切换到闭合状态 * @param failureThreshold * @param consecutiveSuccessThreshold * @param timeout */ public BreakerManager(int failureThreshold, int consecutiveSuccessThreshold, int timeout) { if (failureThreshold < 1 || consecutiveSuccessThreshold < 1) { throw new RuntimeException("熔断器闭合状态的最大失败次数和半熔断状态的最大连续成功次数必须大于0!"); } if (timeout < 1) { throw new RuntimeException("熔断器断开状态超时时间必须大于0!"); } this.failureThreshold = failureThreshold; this.consecutiveSuccessThreshold = consecutiveSuccessThreshold; this.timeout = timeout; moveToClosedState(); } /** * 该方法用于测试 * 经过AttempCall调用,传入指望执行的代理方法,该方法的执行受熔断器保护。这里使用了锁来处理并发问题 */ public void attemptCall(boolean rs, int times) { for(int i=0; i<times; i++) { //须要加同步锁 state.protectedCodeIsAboutToBeCalled(); try { //调用服务 if(!rs) { throw new Exception(); } else { System.out.println("第"+(i+1)+"服务调用成功!"); } } catch (Exception e) { //须要加同步锁 System.out.println("第"+(i+1)+"服务调用超时!"); state.ActUponException(); } //须要加同步锁 state.protectedCodeHasBeenCalled(); } } /** * 手动切换到闭合状态 */ public void close() { //须要加同步锁 moveToClosedState(); } /** * 手动切换到断开状态 */ public void open() { //须要加同步锁 moveToOpenState(); } }
测试类:
public class Test { public static void main(String[] args) { //定义熔断器,失败10次进入断开状态 //在半断开状态下,连续成功15次,进入闭合状态 //5秒后进入半断开状态 BreakerManager manager = new BreakerManager(10, 15, 5000); showState(manager); //模拟失败10次调用 manager.attemptCall(false, 10); System.out.println(manager.failureCount); showState(manager); //这里若是再调用一次服务,正常会抛出“服务已熔断”的异常 //manager.attemptCall(true, 1); //等待熔断器超时,从Open转到HalfOpen try { System.out.println("等待熔断器超时(6s)。。。"); Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } showState(manager); //模拟成功调用15次 manager.attemptCall(true, 10); //这里若是出现一次调用服务失败,熔断器会立刻进入熔断状体,接下来的调用会抛出“服务已熔断”的异常 //manager.attemptCall(false, 1); manager.attemptCall(true, 5); System.out.println(manager.consecutiveSuccessCount); System.out.println(manager.failureCount); showState(manager); } public static void showState(BreakerManager manager) { System.out.println("Breaker is Closed:" + manager.isClosed()); System.out.println("Breaker is Open:" + manager.isOpen()); System.out.println("Breaker is isHalfOpen:" + manager.isHalfOpen()); } }
测试结果:
Breaker is Closed:true Breaker is Open:false Breaker is isHalfOpen:false 第1服务调用超时! 第2服务调用超时! 第3服务调用超时! 第4服务调用超时! 第5服务调用超时! 第6服务调用超时! 第7服务调用超时! 第8服务调用超时! 第9服务调用超时! 第10服务调用超时! 10 Breaker is Closed:false Breaker is Open:true Breaker is isHalfOpen:false 等待熔断器超时(6s)。。。 Breaker is Closed:false Breaker is Open:false Breaker is isHalfOpen:true 第1服务调用成功! 第2服务调用成功! 第3服务调用成功! 第4服务调用成功! 第5服务调用成功! 第6服务调用成功! 第7服务调用成功! 第8服务调用成功! 第9服务调用成功! 第10服务调用成功! 第1服务调用成功! 第2服务调用成功! 第3服务调用成功! 第4服务调用成功! 第5服务调用成功! 15 0 Breaker is Closed:true Breaker is Open:false Breaker is isHalfOpen:false
参考文章:
http://www.cnblogs.com/yangecnu/p/Introduce-Circuit-Breaker-Pattern.html
http://martinfowler.com/bliki/CircuitBreaker.html
http://msdn.microsoft.com/en-us/library/dn589784.aspx
https://yq.aliyun.com/articles/7443
http://www.tuicool.com/articles/AbiqEnn