目录

springcloudAlibaba_sentinel @SentinelResource注解详解

按资源名称&url地址限流

接口代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public CommonResult byResource() {
    return CommonResult.success("按资源名称限流测试OK");
}

@GetMapping("/byUrl")
@SentinelResource(value = "byUrl", blockHandler = "handleException")
public CommonResult byUrl() {
    return CommonResult.success("按url限流测试OK");
}

public CommonResult handleException(BlockException exception) {
    return CommonResult.error(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
}

按资源名称配置限流规则

http://img.cana.space/picStore/20201115203613.png

按url地址限流

http://img.cana.space/picStore/20201115203754.png

狂刷两个接口,结果如下:

http://localhost:8401/byUrl

1
Blocked by Sentinel (flow limiting)

http://localhost:8401/byResource

1
2
3
4
5
{
  "code": 444,
  "desc": "com.alibaba.csp.sentinel.slots.block.flow.FlowException\t 服务不可用",
  "data": null
}

结论

按url地址限流,blockHandler不生效,只会进行默认处理,使用资源名进行限流,会使用自定义的blockHandler方法。

问题

  • 自定义的处理方法和业务耦合在一起,不直观
  • 每个业务方法都添加一个兜底的方法,那代码膨胀加剧
  • 全局统一的处理方法没有体现

客户自定义限流处理逻辑

创建customerBlockHandler类用于自定义限流处理逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package com.eh.cloudalibaba.sentinel;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.eh.cloud2020.common.entity.CommonResult;

/**
 * 自定义限流处理类
 */
public class CustomBlockHandler {
    public static CommonResult handlerException1(BlockException exception) {
        return CommonResult.error(444, "按客戶自定义,global handlerException----1");
    }

    public static CommonResult handlerException2(BlockException exception) {
        return CommonResult.error(444, "按客戶自定义,global handlerException----2");
    }
}

修改@SentinelResource属性配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandlerClass = CustomBlockHandler.class, blockHandler = "handlerException1")
public CommonResult byResource() {
    return CommonResult.success("按资源名称限流测试OK");
}

@GetMapping("/byUrl")
@SentinelResource(value = "byUrl", blockHandlerClass = CustomBlockHandler.class, blockHandler = "handlerException1")
public CommonResult byUrl() {
    return CommonResult.success("按url限流测试OK");
}

再次狂刷两个接口,可以看到

按资源名称配置限流规则的接口访问会使用自定义处理

1
2
3
4
5
{
  "code": 444,
  "desc": "按客戶自定义,global handlerException----1",
  "data": null
}

按url地址配置限流规则的接口访问则依然会使用系统默认处理

1
Blocked by Sentinel (flow limiting)

@SentinelResource 属性介绍

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {

    /**
     * @return name of the Sentinel resource
     */
    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 {};
}

说明

属性名 是否必填 说明
value 资源名称 。(必填项,需要通过 value 值找到对应的规则进行配置)
entryType entry类型,标记流量的方向,取值IN/OUT,默认是OUT
blockHandler 处理BlockException的函数名称(可以理解为对Sentinel的配置进行方法兜底)。函数要求: 1.必须是 public 修饰 2.返回类型与原方法一致 3. 参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。 4. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。
blockHandlerClass 存放blockHandler的类。 对应的处理函数必须 public static 修饰,否则无法解析,其他要求:同blockHandler。
fallback 用于在抛出异常的时候提供fallback处理逻辑(可以理解为对Java异常情况方法兜底)。 fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求: 1.返回类型与原方法一致 2.参数类型需要和原方法相匹配,Sentinel 1.6开始,也可在方法最后加 Throwable 类型的参数。 3.默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定fallbackClass里面的方法。
fallbackClass 存放fallback的类。 对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。
defaultFallback 用于通用的 fallback 逻辑。 默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求: 1.返回类型与原方法一致 2.方法参数列表为空,或者有一个 Throwable 类型的参数。 3.默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定 fallbackClass 里面的方法。
exceptionsToIgnore 指定排除掉哪些异常。 排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。
exceptionsToTrace 需要trace的异常

注意

  1. fallback只用来处理与Java逻辑异常相关的兜底。比如:NullPointerException、ArrayIndexOutOfBoundsException 等Java代码中的异常,fallback 指定的兜底方法便会生效。
  2. blockHandler 只用来处理 与 Sentinel 配置有关的兜底。比如:配置某资源 QPS =1,当 QPS >1 时,blockHandler 指定的兜底方法便会生效。
  3. blockHandlerfallbackHandler都进行了配置,则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑。
  4. 使用 exceptionsTolgnore 属性,来 指定某些异常不执行兜底方法,直接显示错误信息。配置 ArithmeticException 异常不走兜底方法。java.lang.ArithmeticException: / by zero ,便不会再执行兜底方法,直接显示错误信息给前台页面。
  5. 使用 defaultFallback 来指定通用的 fallback 兜底方法。
    • 如果当前业务配置有 defaultFallbackfallback 两个属性,则优先执行 fallback 指定的方法。
    • 如果 fallback 指定的方法不存在,还会执行 defaultFallback 指定的方法。