注解切面适配模块及实现原理
注解切面
资源可以是一个接口、一个方法或一段代码。除提供适配主流框架的模块外,Sentinel 还支持使用“注解+切面”的方式将一个方法定义为资源,其实现原理与适配主流框架的实现原理大同小异。
使用注解定义资源虽然也很方便,但是依然需要修改代码,需要为每个资源加上注解,这是局部的,而使用适配模块是全局的,只要在项目中添加依赖配置就能生效,但是无法适配所有场景。
@SentinelResource
@SentinelResource 注解用于定义资源,作为切点,可被注释在方法或类上。该注解的源码如下:
value:配置资源名称。
entryType:配置流量类型(IN/OUT)。
resourceType:配置资源类型,如 COMMON_WEB、COMMON_RPC。
blockHandlerClass:配置 BlockException 处理器的类型。
blockHandler:需要与 blockHandlerClass 组合使用,配置 BlockException 处理器的方法名称。
fallbackClass:配置 Fallback 处理器的类型。
fallback:配置 fallback 方法名称,需要与 fallbackClass 组合使用,提供类似于适配 OpenFeign 框架的失败回退机制,可用于限流、熔断后的降级处理。
exceptionsToTrace:指定只追踪哪些类型的异常,不配置会导致 Sentinel 不统计异常指标数据,默认追踪所有类型的异常。
exceptionsToIgnore:与 exceptionsToTrace 正好相反,指定不追踪哪些类型的异常。
被 exceptionsToIgnore 方法包含的异常不仅不会被 Sentinel 统计到异常指标中,也不会调用 Fallback 处理器处理降级,而被 exceptionsToTrace 方法包含的异常才会被 Sentinel 统计到异常指标中,并且调用 Fallback 处理器处理降级。
@SentinelResource 注解只是切点,每个配置项的具体用途还需要从切面类中寻找答案。
SentinelResourceAspect
Sentinel 在 sentinel-annotation-aspectj 模块中实现对 @SentinelResource 注解的支持,因此,要使 @SentinelResource 注解生效,需要在项目中添加 sentinel-annotation-aspectj 模块的依赖,配置如下:
Sentinel 使用 AspectJ 框架实现切面,切面类为 SentinelResourceAspect,如果是在 SpringBoot 项目中使用 sentinel-annotation-aspectj 模块,则只需要将 SentinelResourceAspect 切面类注入 Spring 容器即可,代码如下:
SentinelResourceAspect 切面类的源码如下:
获取 @SentinelResource 注解,从注解中取得资源名称、流量类型和资源类型。
调用 SphU#entry 方法。
处理 BlockException,如果注解配置了 blockHandlerClass 和 blockHandler,则使用反射调用 BlockException 处理器方法。
处理执行资源方法抛出的异常,根据 exceptionsToIgnore 和 exceptionsToTrace 配置项决定是否将此异常统计到异常指标中。
调用 Entry#exit 方法。
SentinelResourceAspect#invokeResourceWithSentinel 方法中调用的 handleBlockException 方法与 handleFallback 方法的实现相同,都是通过处理器的类型、方法名、方法参数使用反射从 BlockException 处理器或 Fallback 处理器中获取方法的 Method 实例调用,并且要求:
如果未配置处理器类型或者配置的处理器类型是当前拦截方法所属类,则从当前拦截方法所属类中寻找方法,否则从指定处理器类型中寻找方法,但要求方法是一个静态方法。
方法的参数类型与拦截方法的参数类型相匹配,并且多出一个接收异常的参数。
如果资源方法的方法描述符为:
则 blockHandler 方法的方法描述符必须匹配:
fallback 方法的方法描述符必须匹配:
@SentinelResource 注解支持注释在类或方法上,如果是注释在类上,且通过 value 属性指定了资源名称,则该类下的所有 public 方法都会被切面拦截,并且都会被当作同一个资源处理,而如果未通过 value 属性指定资源名称,则自动根据类名、方法名称和方法参数生成资源名称,代码如下:
显然,这样的资源名称非常不便于进行规则配置,因此在使用 @SentinelResource 注解时最好自行定义资源名称。
除了使用 sentinel-annotation-aspectj 模块支持 @SentinelResource 注解,也可以自行实现切面类,指定全局 BlockException 处理器和 Fallback 处理器,而不必在同一个项目中为每个 @SentinelResource 注解都配置 BlockException 处理器和 Fallback 处理器,并且重写切面的实现也比较简单。
评论