Sentinel提供了@SentinelResource
注解用于定義注解阱驾,并提供了AspectJ的擴(kuò)展用于自定義資源治拿、處理BlockException
等省骂。
注解解釋:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
// 定義的資源名
String value() default "";
// entry類型举户, 默認(rèn)是 EntryType.OUT
EntryType entryType() default EntryType.OUT;
// 資源類型
int resourceType() default 0;
// 對(duì)應(yīng)處理blockException的函數(shù)名稱
String blockHandler() default "";
// 當(dāng)希望使用其他類的函數(shù)時(shí)尤仍,可以通過這個(gè)來指定對(duì)應(yīng)類的Class對(duì)象蕉世,注意對(duì)應(yīng)的函數(shù)必須為static函數(shù)
Class<?>[] blockHandlerClass() default {};
// fallback函數(shù)名稱蔼紧,用于在拋出異常時(shí)提供fallback處理邏輯,可以處理所有類型的異常
String fallback() default "";
// 默認(rèn)的fallback函數(shù)名稱
String defaultFallback() default "";
// 當(dāng)使用其他類的函數(shù)時(shí)
Class<?>[] fallbackClass() default {};
// 要跟蹤的異常類列表
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
// 忽略這些異常
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
當(dāng)出現(xiàn)異常執(zhí)行上述注解中的指定的處理函數(shù)時(shí)狠轻,這時(shí)我們就要利用Spring的AOP特性奸例。
先定義個(gè)切入點(diǎn):
@Aspect
public class SentinelResourceAspect extends AbstractSentinelAspectSupport {
@Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")
public void sentinelResourceAnnotationPointcut() {
}
方法執(zhí)行時(shí)的處理:
@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 = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());
Object result = pjp.proceed();
return result;
} catch (BlockException ex) {
return handleBlockException(pjp, annotation, ex);
} 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;
}
if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
traceException(ex);
return handleFallback(pjp, annotation, ex);
}
// No fallback function can handle the exception, so throw it out.
throw ex;
} finally {
if (entry != null) {
entry.exit(1, pjp.getArgs());
}
}
}
簡(jiǎn)單的說,就是先獲取加了該注解的方法向楼,然后解析獲取注解里面的參數(shù)名等信息查吊,然后根據(jù)獲取的信息執(zhí)行一個(gè)Sphu.entry()
方法包裝資源,判斷是否需要限流等操作湖蜕,可以通過的話逻卖,再利用反射執(zhí)行業(yè)務(wù)邏輯代碼;如果沒有通過限流等操作昭抒,就會(huì)拋出BlockException
的子類異常评也,然后獲取在注解中定義的函數(shù)方法名,利用反射機(jī)制再進(jìn)行執(zhí)行灭返。
protected Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex)
throws Throwable {
// Execute block handler if configured.
Method blockHandlerMethod = extractBlockHandlerMethod(pjp, annotation.blockHandler(),
annotation.blockHandlerClass());
if (blockHandlerMethod != null) {
Object[] originArgs = pjp.getArgs();
// Construct args.
Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1);
args[args.length - 1] = ex;
try {
if (isStatic(blockHandlerMethod)) {
return blockHandlerMethod.invoke(null, args);
}
return blockHandlerMethod.invoke(pjp.getTarget(), args);
} catch (InvocationTargetException e) {
// throw the actual exception
throw e.getTargetException();
}
}
// If no block handler is present, then go to fallback.
return handleFallback(pjp, annotation, ex);
}
參考文章:
注解支持