Sentenel对Spring框架支持
我们经常使用@SentinelResource
来标记一个方法,可以将这个被@SentinelResource
标记的方法看成是一个Sentinel资源
。
因此,我们以@ SentinelResource
为入口,找到其切面,看看切面拦截后所做的工作,来明确Sentinel的工作原理。直接看注解@SentinelResource的切面代码(SentinelResourceAspect)。
(1) Spring中Sentinel使用
<!-- sentinel核心模块-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.6</version>
</dependency>
<!-- sentinel注解 spring里会用到 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.6</version>
</dependency>
配置对应Bean
@Configuration
public class SentinelAspectConfiguration {
/**
*
* @return
*/
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
(1.1) 限流
/**
* 下单接口
*/
@PostMapping(value = "/order/create", consumes = MediaType.APPLICATION_JSON_VALUE)
@SentinelResource(value = "orderCreate", blockHandler = "blockHandlerForOrderCreate")
public OrderResponse<OrderCreateVo> orderCreate(@RequestBody OrderCreateRequest req) {
log.info("下单请求开始||req={}", req);
OrderResponse<OrderCreateVo> res = orderCreateService.createOrder(req);
log.info("下单请求结束||res={}", res);
return res;
}
/**
* 限流
*
* @param req OrderCreateRequest
* @param e BlockException
* @return
*/
public OrderResponse<OrderCreateVo> blockHandlerForOrderCreate(@RequestBody OrderCreateRequest req, BlockException e) {
log.info("下单达到阈值被限流 || ");
return OrderResponse.fail(BizErrorCodeEnum.SYSTEM_BUSY.getCode(), BizErrorCodeEnum.SYSTEM_BUSY.getDescription());
}
(1.2) 熔断
/**
* @param goodsBizReq
* @return
*/
@Override
@SentinelResource(value = "order_getGoodsInfo", fallback = "fallbackForGetGoodsInfo")
public List<GoodsBizResEntity> getGoodsInfo(GoodsRequestBizEntity goodsBizReq) {
// 省略部分代码
return goodsBizList;
}
/**
* 熔断
*
* @param goodsBizReq
* @param ex
* @return
*/
public List<GoodsBizResEntity> fallbackForGetGoodsInfo(GoodsRequestBizEntity goodsBizReq, BlockException ex) {
log.warn("查询商品触发熔断||");
// 走熔断逻辑 调getGoodsInfoV2
return getGoodsInfoV2(goodsBizReq);
}
(2) 源码解析
(2.1) SentinelResourceAspect
package com.alibaba.csp.sentinel.annotation.aspectj;
/**
* SentinelResource注解切面
*/
@Aspect
public class SentinelResourceAspect extends AbstractSentinelAspectSupport {
/** 指定切入点为@SentinelResource注解 */
@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);
// 注解里没配置 默认是OUT
EntryType entryType = annotation.entryType();
// 默认为0
int resourceType = annotation.resourceType();
Entry entry = null;
try {
// 进行流控
entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());
// 通过流控检查后,才会调用目标方法
return pjp.proceed();
} catch (BlockException ex) {
// 对BlockException进行回调
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());
}
}
}
}
进入SentinelResource切面后,会执行SphU.entry()
方法,在这个方法中会对被拦截方法做限流和熔断的逻辑处理。
如果触发限流或熔断,会抛出BlockException
,我们可以指定blockHandler方法来处理BlockException。而对于业务上的异常,我们也可以配置fallback方法来处理被拦截方法调用产生的异常。
public abstract class AbstractSentinelAspectSupport {
/** 获取资源名 */
protected String getResourceName(String resourceName, /*@NonNull*/ Method method) {
// 注解里配置了资源名,使用此值
if (StringUtil.isNotBlank(resourceName)) {
return resourceName;
}
// 解析方法名 使用方法名作为资源名
return MethodUtil.resolveMethodName(method);
}
}
(2.2) SentinelResource
package com.alibaba.csp.sentinel.annotation;
/**
* 注释表示Sentinel资源的定义。
*
* @since 0.1.1
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
/**
* 返回Sentinel资源的名称
*/
String value() default "";
/**
* @return the entry type (inbound or outbound), outbound by default
*/
EntryType entryType() default EntryType.OUT;
/**
* @return the classification (type) of the resource
* @since 1.7.0
*/
int resourceType() default 0;
/**
* @return name of the block exception function, empty by default
*/
String blockHandler() default "";
/**
* The {@code blockHandler} is located in the same class with the original method by default.
* However, if some methods share the same signature and intend to set the same block handler,
* then users can set the class where the block handler exists. Note that the block handler method
* must be static.
*
* @return the class where the block handler exists, should not provide more than one classes
*/
Class<?>[] blockHandlerClass() default {};
/**
* @return name of the fallback function, empty by default
*/
String fallback() default "";
/**
* The {@code defaultFallback} is used as the default universal fallback method.
* It should not accept any parameters, and the return type should be compatible
* with the original method.
*
* @return name of the default fallback method, empty by default
* @since 1.6.0
*/
String defaultFallback() default "";
/**
* The {@code fallback} is located in the same class with the original method by default.
* However, if some methods share the same signature and intend to set the same fallback,
* then users can set the class where the fallback function exists. Note that the shared fallback method
* must be static.
*
* @return the class where the fallback method is located (only single class)
* @since 1.6.0
*/
Class<?>[] fallbackClass() default {};
/**
* @return the list of exception classes to trace, {@link Throwable} by default
* @since 1.5.1
*/
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
/**
* Indicates the exceptions to be ignored. Note that {@code exceptionsToTrace} should
* not appear with {@code exceptionsToIgnore} at the same time, or {@code exceptionsToIgnore}
* will be of higher precedence.
*
* @return the list of exception classes to ignore, empty by default
* @since 1.6.0
*/
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
参考资料
[1] 主流框架的适配