springcloudAlibaba_sentinel 流控
流控规则
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
resource
:资源名,即限流规则的作用对象;名称唯一,默认请求路径count
: 限流阈值grade
: 限流阈值类型(QPS 或并发线程数)- QPS:当调用该api的qps超过阈值的时候进行限流
- 线程数:当调用该api的线程数超过阈值的时候进行限流
limitApp
: 流控针对的调用来源,若为default
则不区分调用来源strategy
: 流控模式,调用关系限流策略- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只限制指定链路上的流量,指定资源从入口资源进来的流量,如果达到阈值就进行限流(api级别的针对来源)
controlBehavior
: 流量控制效果(直接拒绝、Warm Up、匀速排队)- 快速失败:直接失败,抛异常
- Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的qps阈值
- 排队等待:匀速排队方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
如何加流控规则
- 流控规则->新增流控规则
- 簇点链路->对某个链路
+流控
(推荐)
流控模式
直接
QPS直接失败(默认)
新增流控规则
先按照1s一次正常访问http://localhost:8401/hello
狂刷结果:
编辑流控规则qps阈值到100,再次狂刷,发现修改的规则已生效。
思考
直接调用报默认错信息,技术方面OK but,是否应该有我们自己的后续处理,类似豪猪的fallback兜底方法
线程数直接失败
-
改造服务接口,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
@SpringBootApplication public class ServiceMain8401 { public static void main(String[] args) { SpringApplication.run(ServiceMain8401.class, args); } @RestController public class TestController { @GetMapping(value = "/hello") @SentinelResource("hello") public String hello() throws InterruptedException { // sleep 3秒,让应用程序同时存在多个线程 TimeUnit.SECONDS.sleep(3); return "Hello Sentinel"; } } }
-
添加线程数直接失败流控规则
-
狂刷:http://localhost:8401/hello
qps和线程数流控两种方式比较
- qps:御敌于国门之外,还没进到应用里面
- 线程数:关门打狗,进到应用里面但是线程数需要在控制范围内
关联
别人惹事我买单
应用场景:在分布式应用链路中,应用之间是相互影响的,下游(这里使用/world模拟下游
)压力大,需要限制上游(/hello
)流量
-
改造主启动类
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
package com.eh.cloudalibaba.sentinel; import com.alibaba.csp.sentinel.annotation.SentinelResource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class ServiceMain8401 { public static void main(String[] args) { SpringApplication.run(ServiceMain8401.class, args); } @RestController public class HelloController { @GetMapping(value = "/hello") @SentinelResource("hello") public String hello() { return "Hello Sentinel"; } } @RestController public class WorldController { @GetMapping(value = "/world") @SentinelResource("world") public String hello() { return "World Sentinel"; } } }
-
配置关联流控规则
当资源
/world
超过阈值时,/hello
就会被限流 -
验证
当下游(
/world
)qps超过阈值,上游(/hello
)限流使用jmeter模拟下游qps超过1的情况,如下:
-
增加吞吐量定时器,吞吐量控制在1个线程每秒60次请求
-
设置线程数和启动限制时间
-
设置请求
-
检查请求速率
-
访问上游("/hello")
-
关闭jmeter下游请求,再次访问上游
-
链路
链路的控制指的就是对一条链路的访问进行控制。
比方说,有一个二叉树:
a | ||||
---|---|---|---|---|
/ | \ | |||
b | c | |||
/ | | | | | \ | |
d | e | f | g |
a->b->d, a->b->e, a->c->f, a->c->g均可视作链路。假设我以a为入口资源,d为终点资源,对这条链路进行限制的话,则资源a,b,d均会被限制访问。
流控效果
快速失败
默认的流控处理,直接失败,抛出异常Blocked by Sentinel (flow limiting)
源码
|
|
预热(Warm Up)
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
源码
|
|
应用场景
秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是为了保护系统慢慢的把阈值增长到设置的阈值。
演示
-
配置流控效果,5秒内达到10
-
设置jemete进行访问,qps 10
-
验证
可以看到流控从qps为3在5s内慢慢增加到10,最后都能正常访问,这就是流控的预热效果
排队等待
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo。
该方式的作用如下图所示:
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
演示:
-
配置匀速排队模式流控规则,单机阈值10,超时时间10s
-
设置jemeter,吞吐量20,应该全部正确,如下
可以看到实际吞吐量被限制了,使用的漏桶算法。