Sentinel流量控制官方文档:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
流量控制概述:
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
- resource:资源名,即限流规则的作用对象
- count: 限流阈值 grade: 限流阈值类型(QPS 或并发线程数)
- limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
- strategy: 调用关系限流策略
- controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
流量控制主要有两种统计类型:
- 一种是统计并发线程数,当调用该api的线程数达到阈值的时候,进行限流
- 一种是统计 QPS(每秒钟的请求数量),当调用该api的QPS达到阈值的时候,进行限流
是否集群:不需要集群
流控模式:
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
流控效果:
- 快速失败:直接失败,抛异常
- Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
流控规则实战:
(一)QPS-直接-快速失败:设置每秒请求数量为 1,那么如果超过这个单机阈值,则sentinel会快速响应失败
(二)线程数-直接失败:设置处理用户请求的线程数量,如果在一定时间内请求数量增多,而达到线程处理阈值,就会出现流控。比如:设置线程数量为1,需要处理用户请求时间800ms,但是在800ms内出现多个请求需要处理,则sentinel就会进行流控
通过JMeter 进行的线程的并发测试
(三)流控-关联
- 概念:当关联的资源达到阈值时,就限流自己
- 例如:当与A服务关联的资源B达到阀值后,就限流A自己。泛指如果B服务挂掉,A服务就开启对自己的限流
- 例如:支付接口达到阈值,则就是限流下订单的接口
我们在FlowLimitController接口中再去定义一个请求 public String playInfo() 方法
package com.liuyangjava.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class FlowLimitController { @Value("${server.port}") private String port; @GetMapping("/sentinel/service") public String getInfo() { return "sentinel service is running, server port: " + port; } @GetMapping("/sentinel/playInfo") public String playInfo() { return "sentinel service is playInfo"; } }
开始配置流控-关联资源模式,当关联资源 /sentinel/playInfo 的 qps 阀值超过1时,就限流 /sentinel/service 的Rest访问地址
通过JMeter 进行的线程的并发测试,模拟50个用户,每个用户请求20次,每秒并发5个请求
- Number of Threads (users):运行的线程数设置,一个线程对应一个模拟用户
- Ramp-up period (seconds):所有线程在多长时间内开始运行,单位是秒。比如我们设置线程数为 50,此处设置 10 秒,那么每秒就会启动 50 / 10,5 个线程。如果设置为 0 秒,则 50 个线程会立刻启动。如果设置为 100 秒,就会每隔 100 / 50,2 秒 启动 1 个线程
- Loop Count:迭代次数。可以填写数字指定迭代次数。也可以勾选 Infinite,表示无限迭代,一直运行到测试停止或异常崩溃
通过上面的模拟,当请求达到阈值,则 /sentinel/service 就会对自己进行限流控制
(四)预热(warm up)
当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮
默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值
预热冷启动的文档地址:https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8
(五)排队等待
匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。官方对排队等待的一个图解如下
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
稍微修改一下 FlowLimitController ,增加日志的输出信息
package com.liuyangjava.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class FlowLimitController { @Value("${server.port}") private String port; private Logger logger = LoggerFactory.getLogger(FlowLimitController.class); @GetMapping("/sentinel/service") public String getInfo() { return "sentinel service is running, server port: " + port; } @GetMapping("/sentinel/playInfo") public String playInfo() { logger.info(Thread.currentThread().getName()+" "+".../sentinel/playInfo"); return "sentinel service is playInfo"; } }
最后通过JMeter进行压力测试,后端输出的接口,几乎就是2秒钟通过2个请求