Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效(服务重启后失效)。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略。

流量控制规则 (FlowRule) 的重要属性

Field说明默认值
resource资源名,资源名是限流规则的作用对象
count限流阈值
grade限流阈值类型,QPS 或线程数模式QPS 模式
limitApp流控针对的调用来源default,代表不区分调用来源
strategy调用关系限流策略:直接、链路、关联根据资源本身(直接)
controlBehavior流控效果(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流直接拒绝

同一个资源可以同时有多个限流规则。

通过代码定义流量控制规则

理解上面规则的定义之后,我们可以通过调用 FlowRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则,比如:

private static void initFlowQpsRule() {
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule1 = new FlowRule();
    rule1.setResource(resource);
    // Set max qps to 20
    rule1.setCount(20);
    rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule1.setLimitApp("default");
    rules.add(rule1);
    FlowRuleManager.loadRules(rules);
}

更多详细内容可以参考 流量控制

通过控制台添加流控规则

在添加限流规则时,点击高级选项,可以选择三种流控模式:

  • 直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
  • 关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
  • 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流

关联模式

  • 关联模式
    • 统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流。
  • 使用场景
    • 比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是有限支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。

示例

  • 在 OrderController 有两个端点:/order/query 和 /order/update
  • 配置流控规则,当 /order/update 资源被访问的 QPS 超过 5 时,对 /order/query 请求限流

/write 资源访问量触发阈值时,就会对 /read 资源限流,避免影响 /write 资源。

满足下面条件可以使用关联模式:

  • 两个有竞争关系的资源
  • 一个优先级较高,一个优先级较低

链路模式

链路模式:只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。 例如有两条请求链路:

  • /test1/common
  • /test2/common

示例

有查询订单和创建订单业务,两者都需要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。 步骤:

  1. 在 OrderService 中添加一个 queryGoods 方法
  2. 在 OrderController 中,改造 /order/query 端点,调用 OrderService 中的 queryGoods 方法
  3. 在 OrderController 中添加一个 /order/save 的端点,调用 OrderService 的 queryGoods 方法
  4. queryGoods 设置限流规则,从 /order/query 进入 queryGoods 的方法限制 QPS 必须小于 2

注意 Sentinel 默认只标记 Controller 中的方法为资源,如果要标记其它方法,需要利用 @SentinelResource 注解。

@SentinelResource("goods")
public Map<String, String> queryGoods() {
	return Map.of("msg", "查询成功");
}

Sentinel 默认会将 Controller 方法做 context 整合,导致链路模式的流控失效,需要修改 application.yml,添加配置。

spring:
  cloud:
    sentinel:
      web-context-unify: false # 关闭 context 整合

使用 jmeter 压力测试工具 测试限流效果

又是被自己蠢到的一天

脑子想测试链路模式,结果手点的是关联模式。 我说怎么限流不生效,还以为是 Sentinel 的坑。

限流效果

/order/query 被限流

/order/save 全部正常

Sentinel 控制台

总结

  • 直接:对当前资源限流
  • 关联:高优先级资源触发阈值,对低优先级资源限流
  • 链路:阈值统计时,只统计从指定资源进入当前资源的请求,是对请求来源的限流

流控效果

流控效果是指请求达到流控阈值时应该采取的措施,包括三种:

  • 快速失败:达到阈值后,新的请求会被立即拒绝并抛出 FlowException.异常。是默认的处理方式。
  • warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。
  • 排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长

warm up

warm up 也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是 threshold/coldFactor,持续指定时长后,逐渐提高到 threshold 值,而 coldFactor 的默认值是 3。

示例

新增规则

新增计划

监控数据

排队等待

当请求超过 QPS 阈值时,快速失败和 warm up 会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。

例如:QPS=5,意味着每 200ms 处理一个队列中的请求;timeout=2000,意味着预期等待超过 2000ms 的请求会被拒绝并抛出异常。

示例

新增规则

新增计划

QPS 稳定在 10

总结

  • 快速失败:QPS 超过阈值时,拒绝新的请求
  • warm up:QPS 超过阈值时,拒绝新的请求;QPS 阈值是逐渐提升的,可以避免冷启动时高并发导致服务宕机
  • 排队等待:请求会进入队列,按照阈值允许的时间间隔依次执行请求;如果请求预期等待时长大于超时时间,直接拒绝

热点参数限流

之前的限流是统计访问某个资源的所有请求,判断是否超过 QPS 阈值。而热点参数限流是分别统计参数值相同的请求,判断是否超过 QPS 阈值。

注意

热点参数限流对默认的 SpringMVC 资源无效,需要使用其他手段标记资源,比如 @SentinelResource

示例/order/{id} 这个资源添加热点参数限流,规则如下:

  • 默认的热点参数规则是每 1 秒请求量不超过 2
  • 给 102 参数设置:每 1 秒请求量不超过 4
  • 给 103 参数设置:每 1 秒请求量不超过 10

接口

@RequestMapping("order")
@RestController
public class OrderController {
 
    @SentinelResource("hot")
    @GetMapping(value = "/{id}")
    public Map<String, Long> OrderGetById(@PathVariable("id") Long id) {
        return Map.of("id", id);
    }
}

新增热点规则

新增计划

默认 QPS 是 2

id=102,QPS=4

id=103,QPS=10

监控数据

参考链接