DegradeSlot是用于服务降级熔断。
在执行entry的过程中,对于处于熔断open状态的情况则判断是否已经过了熔断期且设置半开成功,那么就通过.否则不通过报DegradeException
对于处于降级状态即half-open的时候,则直接抛出DegradeException.
Sentinel的熔断器一共有两种ExceptionCircuitBreaker 和 ResponseTimeCircuitBreaker 都 extends AbstractCircuitBreaker implements CircuitBreaker
在上一个之前slot执行过程中,如果发生了非BlockException即一些未知的throw,那么在exit内会判断error是否达到配置的erro数量或者错误比例。
如果整个调用过程超过了配置的超时时间 则也会触发熔断。
熔断的目的是将熔断器的状态设置到半开或者全开,这样在tryPass校验的时候就可以返回通过或者异常了。
配置面板如下:
根据配置项,可以具体看一下熔断器的接口 CircuitBreaker
public interface CircuitBreaker { /** * 降级熔断规则 */ DegradeRule getRule(); /** * true 判断需要降级 */ boolean tryPass(Context context); /** * 当前熔断器的状态 */ State currentState(); /** * 回调方法 当请求pass通过后触发 */ void onRequestComplete(Context context); /** * Circuit breaker state. */ enum State { OPEN, HALF_OPEN, CLOSED } }
了解了熔断规则以后,下面将具体阐述熔断流程。
在DegradeSlot#entry#performChecking中,先根据资源名称获取到所有的熔断器列表,然后逐个校验。
void performChecking(Context context, ResourceWrapper r) throws BlockException { // 根据资源名 获取该资源的熔断器列表 List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName()); if (circuitBreakers == null || circuitBreakers.isEmpty()) { return; } for (CircuitBreaker cb : circuitBreakers) { //逐次校验 if (!cb.tryPass(context)) { throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule()); } } }
这里面怎么获取到熔断配置,后续在控制台这章会详细讲解,关于页面上的配置如何在服务中生效。接下来重点看一下tryPass
@Override public boolean tryPass(Context context) { // Template implementation. if (currentState.get() == State.CLOSED) { return true; } // 若下次时间窗到达,且状态由open-> halfopen 则返回true if (currentState.get() == State.OPEN) { // For half-open state we allow a request for probing. // 校验是否通过的时候 有可能放开 return retryTimeoutArrived() && fromOpenToHalfOpen(context); } return false; }
很好理解,如果熔断没开,直接return,如果发现正处于熔断状态,这个时候回试图去判断熔断当前时间是否已经过了熔断期,如果过了熔断器,那么尝试性的将open状态设置成half_open,如果设置成功,那么就通过。
在statisticSlot中对于一些非BlockException会设置error,然后在调用exit的时候,传递到DegradeSlot#exit的时候,根据情况将会对熔断器的状态设置成open。
这边以ExceptionCircuitBreaker举例,
1 如果当前资源的熔断器已经是open,则不做调整。
2 如果是half-open状态,根据本地调用情况,是否有error来决定设置成close还是升级到熔断open状态
3 如果是close状态,则开始检验降级配置,计算异常数或者异常比例,如果超过阈值则将熔断器状态设置到open
注意:对异常数或者慢调用时间都是以滑动时间窗的数据结构来统计的,可参考滑动时间窗算法
private final LeapArray<SimpleErrorCounter> stat; private final LeapArray<SlowRequestCounter> slidingCounter;
实现代码如下:
@Override public void (Context context) { Entry entry = context.getCurEntry(); if (entry == null) { return; } // 非blockException Throwable error = entry.getError(); // 这边也是使用滑动窗口统计 SimpleErrorCounter counter = stat.currentWindow().value(); if (error != null) { counter.getErrorCount().add(1); } counter.getTotalCount().add(1); handleStateChangeWhenThresholdExceeded(error); } private void handleStateChangeWhenThresholdExceeded(Throwable error) { if (currentState.get() == State.OPEN) { return; } if (currentState.get() == State.HALF_OPEN) { // In detecting request if (error == null) { fromHalfOpenToClose(); } else { fromHalfOpenToOpen(1.0d); } return; } List<SimpleErrorCounter> counters = stat.values(); long errCount = 0; long totalCount = 0; for (SimpleErrorCounter counter : counters) { errCount += counter.errorCount.sum(); totalCount += counter.totalCount.sum(); } if (totalCount < minRequestAmount) { return; } double curCount = errCount; if (strategy == DEGRADE_GRADE_EXCEPTION_RATIO) { // Use errorRatio curCount = errCount * 1.0d / totalCount; } if (curCount > threshold) { transformToOpen(curCount); } }