一.介绍
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
二.基于QPS/并发数的流量控制
流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS
本教程基于SpringCloud Alibaba,所以先配置相关的文件
1.pom.xml引入相关的坐标
<!--Nacos坐标--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--sentinel坐标--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!--sentinel持久化--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
2.yml配置
server: port: 8002 spring: application: name: dream-microservice-consumer cloud: sentinel: transport: dashboard: ip:8080 #sentinel服务地址,仪表盘端口默认为8080 port: 8719 #sentinel服务端口默认为8719,如果冲突了就+1,知道没有端口冲突 nacos: discovery: server-addr: ip:8848 config: server-addr: ip:8848 file-extension: yaml
流控模式
直接模式
1.建立一个简单测试接口
/** * 渣男小四 * @return */ @GetMapping("/flowLimit") public String flowLimit(){ return "FlowLimit Success"; }
2.在仪表盘配置相应的流控规则如下图:
QPS为一秒最多只有一个请求
3.测试接口
如果正常的一秒钟发起一次请求,则会返回正常的信息,如果一秒钟发起多次请求,则会返回sentinel的限流提示。
正常情况:
请求过于频繁的情况:
并发线程数控制
并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。
示例:代码同上,只需要修改sentinel仪表盘,改为线程数,一秒只处理一个线程,如果线程过多,则不能进行处理,直接返回。
每个请求需要处理1秒
如上所示,在程序里面让请求停了一秒钟,然而一秒只能处理一个请求,当有大量的请求来的时候,sentinel就会返回限流提示。
QPS和线程数的区别:就好比某个办理处,人太多了,所以有一扇门挡住,QPS就是如果一秒只允许一个人进来,那么其他人连门都进不了,只有一个人能进门,线程数就是我允许所有的人都进门来,但是我现在只有一个worker,一秒钟只能给一个人办理业务,其他的人办理不了,在程序里面就会报相应的错。
关联模式
流控模式为关联的情况下,当关联的资源达到预置时,就限流自己,就好比如果支付接口到达阈值,就限流下订单的接口,这样就能有效的防止多个资源和接口同时挂掉。
例子:
1.设置两个接口flowLimit1和flowLimit2,当flowLimit2到达阈值时,flowLimit1接口就挂掉。
2.在sentinel仪表盘配置关联,flowLimt1关于flowLimt2
链路模式
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流
流控效果
流量控制的效果包括直接拒绝(默认)、Warm Up、排队等待
快速失败(默认)
当到达限流条件,直接抛出异常(Blocked by Sentinel (flow limiting))
Warm Up(预热)
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。
这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等。
它的实现是在 Guava 的算法的基础上实现的。然而,和 Guava 的场景不同,Guava 的场景主要用于调节请求的间隔,即 Leaky Bucket,而 Sentinel 则主要用于控制每秒的 QPS,即我们满足每秒通过的 QPS 即可,我们不需要关注每个请求的间隔,换言之,我们更像一个 Token Bucket。
我们用桶里剩余的令牌来量化系统的使用率。假设系统每秒的处理能力为 b,系统每处理一个请求,就从桶中取走一个令牌;每秒这个令牌桶会自动掉落b个令牌。令牌桶越满,则说明系统的利用率越低;当令牌桶里的令牌高于某个阈值之后,我们称之为令牌桶"饱和"。
当令牌桶饱和的时候,基于 Guava 的计算上,我们可以推出下面两个公式:
rate(c)=m*c+ coldrate
其中,rate 为当前请求和上一个请求的间隔时间,而 rate 是和令牌桶中的高于阈值的令牌数量成线形关系的。cold rate 则为当桶满的时候,请求和请求的最大间隔。通常是 coldFactor * rate(stable)
。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
默认 coldFactor
为 3(写死的),即请求 QPS 从 threshold / 3
开始,经预热时长逐渐升至设定的 QPS 阈值。
如:默认的冷加载因子(coldFactor
)是3,即请求 QPS 从 threshold / 3
开始(阈值为3),经过5秒的预热时长,QPS从3恢复到10
通俗易懂一点就是:前五秒钟阈值为3,过了五秒钟过后变为10
排队等待:
Leaky Bucket 对应 流量整形 中的匀速器。它的中心思想是,以固定的间隔时间让请求通过。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。
这种方式适合用于请求以突刺状来到,这个时候我们不希望一下子把所有的请求都通过,这样可能会把系统压垮;同时我们也期待系统以稳定的速度,逐步处理这些请求,以起到“削峰填谷”的效果,而不是拒绝所有请求。
例如,我们去食堂里面打饭需要排队,效果如下所示:
Sentinel 匀速排队等待策略是 Leaky Bucket 算法结合虚拟队列等待机制实现的。
匀速排队模式暂时不支持 QPS > 1000 的场景。
如下:设置为每秒允许有10次请求,则每个请求的平均时长为1000 / 10 = 100 ms,如果每秒超过了10个请求,则需要排队等待,每个请求最长等待时间为10s。
至此,sentinel的限流就差不多了