我是靠谱客的博主 感动小懒虫,最近开发中收集的这篇文章主要介绍源码分析 Sentinel DegradeSlot 熔断,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

seatinel core版本为1.7版本
Sentinel的熔断是由slotchain中的最后一个DegradeSlot来实现的

public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
//在触发后续slot前执行熔断的检查
performChecking(resourceWrapper);
//触发后续的slot
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
void performChecking(ResourceWrapper r) throws BlockException {
//通过资源名称获取所有的熔断CircuitBreaker
List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
if (circuitBreakers == null || circuitBreakers.isEmpty()) {
return;
}
for (CircuitBreaker cb : circuitBreakers) {
//cb.tryPass里面只做了状态检查 , 熔断是否关闭或者打开
if (!cb.tryPass()) {
//该异常为BlockException子类
throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());
}
}
}
@Override
public void exit(Context context, ResourceWrapper r, int count, Object... args) {
Entry curEntry = context.getCurEntry();
//如果当前其他solt已经有了BlockException直接调用fireExit 不用继续走熔断逻辑了,注意是BlockException ,如果是其他异常,会是熔断的一个判断条件
if (curEntry.getBlockError() != null) {
fireExit(context, r, count, args);
return;
}
//通过资源名称获取所有的熔断CircuitBreaker
List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
if (circuitBreakers == null || circuitBreakers.isEmpty()) {
fireExit(context, r, count, args);
return;
}
if (curEntry.getBlockError() == null) {
// completeTimestamp 在StatisticSlot.exit里面设置 , 如果没有设置就取当前时间戳,这个没有多大差别,毕竟 Sentinel在 每秒25万并发下才有性能影响,而且这只是相邻的solt,前后时间差不会多很多
long completeTime = curEntry.getCompleteTimestamp();
if (completeTime <= 0) {
completeTime = TimeUtil.currentTimeMillis();
}
// CreateTimestamp在创建entry的时候创建
long rt = completeTime - curEntry.getCreateTimestamp();
// 注意这里的异常是业务异常
Throwable error = curEntry.getError();
for (CircuitBreaker circuitBreaker : circuitBreakers) {
// 下面会跟踪分析
circuitBreaker.onRequestComplete(rt, error);
}
}
fireExit(context, r, count, args);
}
}

要注意进入DegradeSlot只会检查断路器是否已经打开,再根据是否超过了重试时间来开启半开状态,然后就直接返回是否通过。而真正判断是否需要开启断路器的地方时在exit方法里面,因为这个方法是在业务方法执行后调用的,断路器需要收集业务异常或者业务方法的执行时间来判断是否打开断路器。

AbstractCircuitBreaker里面有个recoveryTimeoutMs变量,它是根据DegradeRule配置的timeWindow * 1000得来,也就是根据规则里面配置的降级恢复间隔时间 * 1000得到的秒数。另外AbstractCircuitBreaker还有个nextRetryTimestamp变量,它在断路器没有开启和半开启前一直是0,一但开启后会被更新成TimeUtil.currentTimeMillis() + recoveryTimeoutMs。所以

if (currentState.get() == State.OPEN) {
// For half-open state we allow a request for trial.
return retryTimeoutArrived() && fromOpenToHalfOpen();
}

这段代码的意思是如果断路器开启,但是上一个请求距离现在已经过了重试间隔时间就开启半启动状态。
CircuitBreaker有ExceptionCircuitBreaker和ResponseTimeCircuitBreaker两种类型的断路器,下面分析下比较简单的ExceptionCircuitBreaker


@Override
public void onRequestComplete(long rt, Throwable error) {
//异常时间窗口计数器
SimpleErrorCounter counter = stat.currentWindow().value();
if (error != null) {
//异常数加1
counter.getErrorCount().add(1);
}
//总数加1
counter.getTotalCount().add(1);
handleStateChangeWhenThresholdExceeded(error);
}

这个方法是在DegradeSlot.exit()中被调用的方法。
SimpleErrorCounter counter = stat.currentWindow().value();
获取的是异常时间窗口计数器,代表的指定时间范围内业务异常的数量

private void handleStateChangeWhenThresholdExceeded(Throwable error) {
//断路器已开直接返回
if (currentState.get() == State.OPEN) {
return;
}
//断路器处于半开状态
if (currentState.get() == State.HALF_OPEN) {
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();
}
//请求总数小于minRequestAmount时不做熔断处理 minRequestAmount时配置在熔断规则里面的
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);
}
}

stat.values();这个方法返回了一个时间窗口的集合,Sentinel 会根据intervalInMs来每个一段时间来统计一匹数据也就是一个时间窗口,DegradeSlot 在某一段时间内会有多个时间窗口。

最后

以上就是感动小懒虫为你收集整理的源码分析 Sentinel DegradeSlot 熔断的全部内容,希望文章能够帮你解决源码分析 Sentinel DegradeSlot 熔断所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(114)

评论列表共有 0 条评论

立即
投稿
返回
顶部