@SentinelResource 注解的使用方法
官网文档:Sentinel 注解支持
对应 Spring 中的切面:SentinelResourceAspect
/**
* Aspect for methods with {@link SentinelResource} annotation.
*
* @author Eric Zhao
*/
@Aspect
public class SentinelResourceAspect extends AbstractSentinelAspectSupport {// 抽象的父类包含一些通用的方法
@Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")// 切点
public void sentinelResourceAnnotationPointcut() {
}
@Around("sentinelResourceAnnotationPointcut()")
public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
Method originMethod = resolveMethod(pjp);
SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
if (annotation == null) {
// Should not go through here.
throw new IllegalStateException("Wrong state for SentinelResource annotation");
}
String resourceName = getResourceName(annotation.value(), originMethod);
EntryType entryType = annotation.entryType();
int resourceType = annotation.resourceType();
Entry entry = null;
try {// 每次请求都会创建新的 entry 对象,记录了请求开始的时间,然后会在 slot chain 上经过授权,流控,降级等一系列 check 之后,没通过 check 会抛出对应的异常
entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());// 最后会返回 StatisticsSlot 记录各项统计数据
Object result = pjp.proceed();// 如果请求被拒绝了会抛出 BlockException,直接进入 catch 分支了,也就不会访问资源了
return result;
} catch (BlockException ex) {// DegradeException、FlowException 等异常的父类
return handleBlockException(pjp, annotation, ex);// 处理请求被拒绝的情况 blockHandler,如果没有 blockHandler 则会尝试调用 fallback 方法
} catch (Throwable ex) {// 访问的资源内部抛出了异常,也就是业务异常
Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();// 需要忽略的异常
// The ignore list will be checked first.
if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
throw ex;// 该异常属于 ignore,那么直接上抛,不做处理
}
if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
traceException(ex);// 属于需要 trace 的异常,将该异常放入 entry 中,exit 时去处理
return handleFallback(pjp, annotation, ex);// 调用可能存在的 fallback 方法
}
// No fallback function can handle the exception, so throw it out.
throw ex;
} finally {
if (entry != null) {
entry.exit(1, pjp.getArgs());// entry 和 exit 成对使用,记录最终请求的响应时间
}
}
}
}
从切面中可以看到 Sentinel 的使用大致分 3 步:
-
SphU.entry(...)
请求进来前先调用 entry(...)方法,它有一个String resourceName
参数用来表示要访问的资源。内部先创建一个全新的 Entry 对象,然后进行链式的规则校验:授权、流控、降级、系统保护、黑名单等。
若通过了规则校验,则放行本次请求并记录统计信息;否则抛出BlockException
的子类对象,对应规则链上第一个未通过的规则,开发者通过 catch 这个异常并判断其类别来处理请求被拒绝的业务逻辑。 -
访问资源
通过规则校验的请求则可以访问资源,资源先简单粗暴的理解成将要调用的方法。
若在访问资源的过程中出现了错误,开发者可以 catch Throwable 并用Tracer.trace(ex)
将异常放入对应的 Entry 对象。 -
entry.exit(...)
entry(...)方法和 entry.exit(...)必须成对使用,通常 entry.exit(...) 放在 finally 中,确保它最后会被执行。
该方法对本次请求进行统计收尾工作,比如:计算本次请求的响应时长,处理第 2 步中可能存在的异常等。
Sentinel 主工作流程:
-
流程图:来源 https://blog.csdn.net/prestigeding/article/details/103842382
-
slot chain 架构图: