在上一篇中,我们学习完了 Sentinel 的基本流控规则,这篇我们来研究一下 Sentinel 的高级流控规则。

话不多说,开始今天的学习。

一、概述

控制台打开流控规则的高级选项如下

image-20220128220831623

出现了 流控模式流控效果 配置选项,这些配置项都是什么意思呢?

  • 流控模式:

    • 直接:api 达到限流条件时,直接限流
    • 关联:当关联的资源达到限流阈值时,就限流自己
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到峰值,就进行限流)【api级别的针对来源】
  • 流控效果:

    • 快速失败:直接失败,抛异常
    • Warm Up:根据coldFactor(冷加载因子,默认3)的值,从阈值/coldFactor,经过预热时长,才达到设置的QPS阈值
    • 排队等待:匀速排队,让请求以匀速通过,阈值类型必须设置为QPS,否则无效

接下来,让我们详细研究一下。

二、流控模式

1. 直接

api 达到限流条件时,直接限流

这个没什么好说的,直接 + 快速失败是默认的流控模式,在微服务系列:Spring Cloud Alibaba 之 Sentinel 基本流控规则 这篇中,我们的案例就是使用的这种默认的流控模式。

2. 关联

当关联的资源达到限流阈值时,就限流自己

假设 testA 关联 testB,当与 A 关联的资源 B 达到阈值后,就限流 A 自己,比如支付接口达到阈值时,就去限流下订单接口

@RestController
public class TestController {

    @GetMapping("/testA")
    public String testA(){
        return "testA....";
    }

    @GetMapping("/testB")
    public Object testB(){
        return "testB......";
    }
}

我们在控制台中这样配置

image-20220128224207332

保存之后,我们浏览器访问地址 localhost:9201/testAlocalhost:9201/testB 发现都是正常返回,没有问题,此时我们就要借助工具来模拟了

这里我们使用 postman 来测试,原理是使用 postman 启动一个 runner 来不停的访问着 /testB 接口,使 /testB 资源处于达到阈值的状态,然后我们再浏览器访问 /testA ,就会触发 /testA 的限流,当 /testB 恢复到没有达到阈值的状态时,/testA 访问恢复正常,不再触发限流。

注意:这里 /testB 达到阈值,/testB 并不会请求失败,只是给 /testA 的一种状态

  • postman 中新增一个 collections ,加入一个访问 /testB 请求

image-20220128225130692

  • 点击 Run

image-20220128225311842

  • 设置循环 50 次,间隔时间是 1 毫秒

image-20220128225757841

/testA 触发限流

image-20220128230026953

等 postman 中的 /testB 执行完之后,/testA 接口恢复正常

image-20220128230131094

3. 链路

只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到峰值,就进行限流)

它的功能有点类似于针对来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细。

直接看概念可能不太好理解,我们直接上案例:

3.1、两个接口同时调用资源 testC

@RestController
public class TestController {

    @Autowired
    private ITestServiceImpl testService;

    @GetMapping("/testA")
    public String testA(){
        return testService.testC();
    }

    @GetMapping("/testB")
    public Object testB(){
        return testService.testC();
    }
}

3.2、@SentinelResource 定义资源 testC

public interface ITestService {
    String testC();
}

@Service
public class ITestServiceImpl implements ITestService {

    @Override
    @SentinelResource(value = "testC", blockHandler = "testCBlockHandler")
    public String testC() {
        return "调用 testC 资源";
    }

    public String testCBlockHandler(BlockException ex)
    {
        return "testC 触发限流...........";
    }
}

3.3、配置文件:禁止收敛URL的入口 context

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

说实话这里有个坑,低版本可能不生效

从1.6.3 版本开始,Sentinel Web filter 默认收敛所有URL的入口 context,因此链路限流不生效。

1.7.0 版本开始(对应SCA的2.1.1.RELEASE),官方在 CommonFilter 引入了WEB_CONTEXT_UNIFY 参数,用于控制是否收敛context。将其配置为 false 即可根据不同的URL 进行链路限流。

本篇文章使用的 SCA 是 2.2.5 版本,也就是可以直接用这个 web-context-unify: false 来使链路限流生效

<spring-cloud-alibabersion>2.2.5.RELEASE</spring-cloud-alibaba.version>

3.4、启动项目测试

启动项目,访问一下 localhost:9201/testA 地址,观察控制台

image-20220129135714552

树状视图下,可以看到资源 /testA 调用了资源 testC,此时我们给资源 testC 添加链路限流规则

image-20220129140000032

意思是,从入口资源 /testA 调用资源 testC 一旦超过阈值,则触发限流

我们来测试一下,浏览器快速刷新 localhost:9201/testA 地址,触发限流

image-20220129140218967

快速刷新 localhost:9201/testB ,不触发限流

image-20220129140357351

注:

虽然在树状视图下显示有两个 testC 资源,但其实是一个,切回列表视图看看

image-20220129140515196

image-20220129140603393

三、流控效果

1. 快速失败

快速失败是默认效果,即当触发限流时,立即抛出异常 FlowException,立即失败。

2. Warm Up

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

设置阈值为 10 ,预热时间是 5 秒,逐渐增加到阈值上限,所以会有一个初始阈值

初始阈值 = 阈值上限 / coldFactor, coldFactor 是冷加载因子,默认为3

image-20220129143146039

上面这个配置的效果就是:在 5 秒内最大阈值是 3(10/codeFactor),超过5秒,最大阈值为10

浏览器快速刷新 localhost:9201/testA 地址,5 秒内会出现限流,5 秒之后阈值上升为 10

image-20220129144040336

注意:

是每次从流量进来时候都会开始"冷启动",也就是一波请求访问完之后,中间间隔了点时间,再来一波请求同样会重新开始冷启动

3. 排队等待

匀速排队,让请求以匀速通过,阈值类型必须设置为QPS,否则无效

排队等待方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

注意:匀速排队模式暂时不支持 QPS > 1000 的场景。

image-20220129153245588

testA 接口打印日志

@GetMapping("/testA")
public String testA(){
    log.info(Thread.currentThread().getName() + "\t" + "testA......");
    return "testA....";
}

控制台设置排队等待规则

image-20220129153047874

当阈值设为 2 的时候,则代表一秒匀速的通过 2 个请求,也就是每个请求平均间隔恒定为 1000 / 2 = 500 ms,每一个请求的最长等待时间(maxQueueingTimeMs)为 1s 。

快速访问地址 localhost:9201/testA,观察控制台日志,每秒确实匀速通过两个请求

image-20220129153609969

但这个 超时时间 我不是很理解什么意思,网上看到的很多都是错的。

流量控制 匀速排队模式 · alibaba/Sentinel Wiki · GitHub

官网上是这么说的

固定的间隔时间让请求通过。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。

不知道有没有小伙伴不吝赐教下!

PS:都看到这里了,点个赞吧,彦祖

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐