Sentine 源码分析之--DegradeSlot

前言:

上一篇我对 Sentinel 中的 FlowSlot 的相关源码进行了分析,本篇我们开始分析 DegradeSlot相关的源码。

在这里插入图片描述

Sentinel 系列文章传送门:

Sentinel 初步认识及使用

Sentinel 核心概念和工作流程详解

Spring Cloud 整合 Nacos、Sentinel、OpenFigen 实战【微服务熔断降级实战】

Sentinel 源码分析入门【Entry、Chain、Context】

Sentine 源码分析之–NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot

Sentine 源码分析之–AuthoritySlot、SystemSlot、GatewayFlowSlot

Sentine 源码分析之–ParamFlowSlot

Sentine 源码分析之–FlowSlot

DegradeSlot

DegradeSlot 是 Slot 责任链上的最后一环,是用来熔断降级的,可以根据慢调用、异常比例和异常数进行熔断,并自定义持续时间以实现系统保护。

DegradeSlot 规则配置如下:

在这里插入图片描述

规则中各个字段的含义在 DegradeRule 源码中都有描述,如下:

public class DegradeRule extends AbstractRule {
	
	//熔断策略 (0: 平均RT 1: 异常比例 2: 异常计数)
    private int grade = RuleConstant.DEGRADE_GRADE_RT;

    //阈值 含义取决于所选择的熔断策略
    private double count;
	
    //断路器断开后恢复时间(单位秒)  超时后断路器转换成半开状态  允许部分请求通过
    private int timeWindow;

    //触发熔断最低请求数  DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT = 5
    private int minRequestAmount = RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT;

    //RT模式下慢请求比例的阈值
    private double slowRatioThreshold = 1.0d;

    //间隔统计持续时间 (毫秒)
    private int statIntervalMs = 1000;
}

断路器的分类

通过上面的规则配置截图,我们可以把断路器分为两类,如下:

  • 异常类型断路器:异常数、异常比例都是异常类型断路器来实现的,在请求结束时统计异常数和请求总数,计算二者比例,判断是否达到阈值,达到阈值更改断路器状态。
  • RT类型断路器:是慢比例调用的断路器的实现,计算请求结束和请求开始的差值,和阈值比较,得到慢调用请求数和请求总数,计算二者比例,判断是否达到阈值,达到阈值更改断路器状态。

断路器的状态

  • OPEN: 断路器打开状态, 系统进入熔断状态。
  • HALF_OPEN:断路器半开状态,系统放行部分请求,如果请求通过,断路器切回到关闭状态, 如果请求出现异常,断路器切回到打开状态。
  • CLAOSE:断路器关闭状态,系统正常。

DegradeSlot#entry 方法源码解析

DegradeSlot#entry 方法同样是执行规则校验,然后执行下一个 Slot(这里其实没有下一个 Slot 了)。

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
				  boolean prioritized, Object... args) throws Throwable {
	//执行检查
	performChecking(context, resourceWrapper);
	//下一个 slot
	fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

DegradeSlot#performChecking 方法源码解析

DegradeSlot#performChecking 方法主要是获取所有断路器,遍历所有短路器,执行规则校验。

//com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot#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());
		}
	}
}

AbstractCircuitBreaker#tryPass 方法源码解析

AbstractCircuitBreaker#tryPass 主要是判断断路器状态,如果短路器状态是关闭的直接返回规则通过,如果短路器是打开的,则执行规则检测。


//com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.AbstractCircuitBreaker#tryPass
@Override
public boolean tryPass(Context context) {
	// Template implementation.
	//断路器是否是关闭状态
	if (currentState.get() == State.CLOSED) {
		//断路器关闭状态 直接返回 通过
		return true;
	}
	if (currentState.get() == State.OPEN) {
		// For half-open state we allow a request for probing.
		//retryTimeoutArrived()  判断当前时间是否大于尝试恢复的时间
		//fromOpenToHalfOpen(context) 尝试将断路器的状态从打开 OPEN 更改为半开启 HALF_OPEN
		return retryTimeoutArrived() && fromOpenToHalfOpen(context);
	}
	return false;
}

AbstractCircuitBreaker#retryTimeoutArrived 方法源码解析

AbstractCircuitBreaker#retryTimeoutArrived 方法主要判断当前时间是否大于尝试恢复的时间,如果大于则返回 true 否则返回 false。

//com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.AbstractCircuitBreaker#retryTimeoutArrived
protected boolean retryTimeoutArrived() {
	//如果当前系统时间大于等于下一次尝试恢复的时间 也就是说已经到达了可以尝试恢复的时间 则返回 true 否则返回 false
	return TimeUtil.currentTimeMillis() >= nextRetryTimestamp;
}

AbstractCircuitBreaker#fromOpenToHalfOpen 方法源码解析

AbstractCircuitBreaker#fromOpenToHalfOpen 尝试将断路器的状态从打开 OPEN 更改为半开启 HALF_OPEN,如果状态切换成功,返回 true 表示请求放行,否则返回 false 表示拒绝请求。


//com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.AbstractCircuitBreaker#fromOpenToHalfOpen
protected boolean fromOpenToHalfOpen(Context context) {
	//设置开放到半开放
	if (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {
		//通知观察者 当前断路器模式从 OPEN 状态变更成了 HALF_OPEN
		notifyObservers(State.OPEN, State.HALF_OPEN, null);
		//获取 从上下文 中获取 entry 对象
		Entry entry = context.getCurEntry();
		//向 Entry 实例注册一个 exit 回调处理器  在 Entry 实例的 exit 方法被调用时回调
		entry.whenTerminate(new BiConsumer<Context, Entry>() {
			@Override
			public void accept(Context context, Entry entry) {
				// Note: This works as a temporary workaround for https://github.com/alibaba/Sentinel/issues/1638
				// Without the hook, the circuit breaker won't recover from half-open state in some circumstances
				// when the request is actually blocked by upcoming rules (not only degrade rules).
				if (entry.getBlockError() != null) {
					// Fallback to OPEN due to detecting request is blocked
					//由于检测到请求被阻止而回退到 OPEN
					//如果当前请求被拒绝 不仅包括熔断器拒绝的 也包括限流、系统自适应等拒绝的 将熔断器从 HALF_OPEN 状态变为 OPEN 状态
					currentState.compareAndSet(State.HALF_OPEN, State.OPEN);
					//通知观察者 当前断路器模式从 HALF_OPEN 状态变更成了 OPEN
					notifyObservers(State.HALF_OPEN, State.OPEN, 1.0d);
				}
			}
		});
		return true;
	}
	return false;
}

熔断降级的时机是触发配置阈值时,那数据是如何收集的呢?DegradeSlot#entry 方法是请求入口,此时请求还没有结束,无法获取到异常请求数量、 RT相关信息, DegradeSlot#exit 作为请求出口方法,此时请求已经结束,相关指标数据都可以收集到了,下面我们来分析一下 DegradeSlot#exit 方法。

DegradeSlot#exit 方法源码解析

DegradeSlot#exit 方法会获取当前 Entry 对象,判断 Entry 对象中是否有阻塞错误,如果有则执行执行下一个 exit 方法,否则获取所有断路器并进行为空判断,如果 Entry 对象中没有阻塞错误,且断路器也不为空,则遍历断路器执行 onRequestComplete 方法进行计数。


//com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot#exit
@Override
public void exit(Context context, ResourceWrapper r, int count, Object... args) {
	//获取当前 Entry
	Entry curEntry = context.getCurEntry();
	//判断 entry 中是否有阻塞错误
	if (curEntry.getBlockError() != null) {
		//如果有就直接下一个 exit 方法 不用在执行断路器的逻辑了
		fireExit(context, r, count, args);
		return;
	}
	//获取所有断路器
	List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
	//为空 判断
	if (circuitBreakers == null || circuitBreakers.isEmpty()) {
		//如果断路器集合为空 也不需要走入校逻辑了
		fireExit(context, r, count, args);
		return;
	}
	//判断 entry 中是否有阻塞错误
	if (curEntry.getBlockError() == null) {
		//阻塞错误为空 遍历执行 onRequestComplete 方法计数
		// passed request
		for (CircuitBreaker circuitBreaker : circuitBreakers) {
			circuitBreaker.onRequestComplete(context);
		}
	}
	//执行下一个出口方法
	fireExit(context, r, count, args);
}

ExceptionCircuitBreaker#onRequestComplete 方法源码解析

ExceptionCircuitBreaker#onRequestComplete 方法的主要作用是计数,当请求结束时候会根据熔断策略来更新计数器,如果达到阀值,则会打开断路器,并通知观察者,重点关注,handleStateChangeWhenThresholdExceeded 方法。

//com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker#onRequestComplete
@Override
public void onRequestComplete(Context context) {
	//获取当前 Entry 对象
	Entry entry = context.getCurEntry();
	//entry 为空判断
	if (entry == null) {
		return;
	}
	//获取 error 信息
	Throwable error = entry.getError();
	//获取当前窗口中的值
	SimpleErrorCounter counter = stat.currentWindow().value();
	//error 为空判断
	if (error != null) {
		//不为空 error 数量 +1
		counter.getErrorCount().add(1);
	}
	//总数 +1
	counter.getTotalCount().add(1);
	//超过阀值时 状态变化
	handleStateChangeWhenThresholdExceeded(error);
}


ExceptionCircuitBreaker#handleStateChangeWhenThresholdExceeded 方法源码解析

ExceptionCircuitBreaker#handleStateChangeWhenThresholdExceeded 方法通过计算异常请求的占比来判断是否需要打开断路器,具体逻辑如下:

  • 如果断路器是打开状态,则直接返回,无需判断异常占比。
  • 如果断路器是半开状态,判断 error 是否为空,如果 error 为空则可以关闭断路器,否则打开断路器。
  • 获取所有 SimpleErrorCounter,计算异常请求数量和请求总数量。
  • 如果请求总数没有达到达到最小请求数,规则直接通过。
  • 计算异常比例,如果异常比例大于阀值,则会打开断路器并通知各个观察者。
//com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker#handleStateChangeWhenThresholdExceeded
private void handleStateChangeWhenThresholdExceeded(Throwable error) {
	//断路器状态判断
	if (currentState.get() == State.OPEN) {
		//状态已经打开了 直接返回
		return;
	}
	
	if (currentState.get() == State.HALF_OPEN) {
		// In detecting request
		//短路器半开状态
		if (error == null) {
			//error 为空 表示可以关闭断路器了 并通知各个观察者
			fromHalfOpenToClose();
		} else {
			//error 不为空 继续打开断路器 并通知各个观察者
			fromHalfOpenToOpen(1.0d);
		}
		return;
	}
	//走到这里说明断路器是关闭状态
	//获取所有错误计数器
	List<SimpleErrorCounter> counters = stat.values();
	//异常请求数量
	long errCount = 0;
	//总请求数量
	long totalCount = 0;
	//遍历错误计数器
	for (SimpleErrorCounter counter : counters) {
		//error 数增加
		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);
	}
}


ConfigCacheService#dumpBeta 方法源码解析

ResponseTimeCircuitBreaker#onRequestComplete 方法的主要作用是记录慢请求数量,具体的慢请求占比的计算逻辑在 handleStateChangeWhenThresholdExceeded 方法中,我们重点关注该方法。

//com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ResponseTimeCircuitBreaker#onRequestComplete
@Override
public void onRequestComplete(Context context) {
	//获取慢请求计数器
	SlowRequestCounter counter = slidingCounter.currentWindow().value();
	//获取当前 entry
	Entry entry = context.getCurEntry();
	//entry 为空判断
	if (entry == null) {
		return;
	}
	//获取请求完成湿巾
	long completeTime = entry.getCompleteTimestamp();
	if (completeTime <= 0) {
		completeTime = TimeUtil.currentTimeMillis();
	}
	//完成时间-创建时间 计算 rt
	long rt = completeTime - entry.getCreateTimestamp();
	//rt 是否大于最大允许 rt
	if (rt > maxAllowedRt) {
		//大于最大允许 rt 慢请求数+1
		counter.slowCount.add(1);
	}
	//请求总数+1
	counter.totalCount.add(1);
	//超过阀值时候的处理
	handleStateChangeWhenThresholdExceeded(rt);
}

ConfigCacheService#dumpBeta 方法源码解析

ResponseTimeCircuitBreaker#handleStateChangeWhenThresholdExceeded 方法通过计算慢请求的占比来判断是否需要打开断路器,具体逻辑如下:

  • 如果断路器是打开状态,则直接返回,无需判断慢请求占比。
  • 如果断路器是半开状态,判断 error 是否为空,如果 error 为空则可以关闭断路器,否则打开断路器。
  • 获取所有 SlowRequestCounter,计算慢请求数量和请求总数量。
  • 如果请求总数没有达到达到最小请求数,规则直接通过。
  • 计算慢请求比例,如果慢请求比例大于阀值,则会打开断路器并通知各个观察者。

//com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ResponseTimeCircuitBreaker#handleStateChangeWhenThresholdExceeded
private void handleStateChangeWhenThresholdExceeded(long rt) {
	//断路器状态判断
	if (currentState.get() == State.OPEN) {
		//断路器打开的状态 直接返回
		return;
	}
	//断路器是否半开
	if (currentState.get() == State.HALF_OPEN) {
		// In detecting request
		// TODO: improve logic for half-open recovery
		if (rt > maxAllowedRt) {
			//rt 大于最大允许 rt 断路器打开
			fromHalfOpenToOpen(1.0d);
		} else {
			//断路器关闭
			fromHalfOpenToClose();
		}
		return;
	}
	//获取所有慢请求计数器
	List<SlowRequestCounter> counters = slidingCounter.values();
	//慢请求数量
	long slowCount = 0;
	//请求总数
	long totalCount = 0;
	//遍历慢请求计数器
	for (SlowRequestCounter counter : counters) {
		//慢请求书+1
		slowCount += counter.slowCount.sum();
		//请求总数+1
		totalCount += counter.totalCount.sum();
	}
	//请求总数是否小于 最小请求数量
	if (totalCount < minRequestAmount) {
		//是 直接放行
		return;
	}
	//计算慢请求占比
	double currentRatio = slowCount * 1.0d / totalCount;
	if (currentRatio > maxSlowRequestRatio) {
		//慢请求占比大于阀值 断路器打开  通知观察者
		transformToOpen(currentRatio);
	}
}

如有不正确的地方请各位指出纠正。

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值