在Spring Cloud Alibaba sentinel的安装与配置中,我们介绍了Sentinel的功能以及安装部署方式。
在Spring Cloud集成Sentinel之基础功能介绍中,我们介绍了如何在Spring Cloud中集成Sentinel。
本文我们在上面的基础上继续深入,介绍一下Sentinel的流量控制功能。
Sentinel流量控制介绍
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。FlowSlot
会根据预设的规则,结合前面NodeSelectorSlot
、ClusterNodeBuilderSlot
、StatisticSlot
统计出来的实时信息进行流量控制。
限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName)
的时候抛出 FlowException
异常。FlowException
是 BlockException
的子类,您可以捕捉BlockException
来自定义被限流之后的处理逻辑。
同一个资源可以创建多条限流规则。FlowSlot
会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:resource
:资源名,即限流规则的作用对象count
: 限流阈值grade
: 限流阈值类型(QPS 或并发线程数)limitApp
: 流控针对的调用来源,若为 default 则不区分调用来源strategy
: 调用关系限流策略controlBehavior
: 流量控制效果(直接拒绝、Warm Up、匀速排队)
名词解释
- 资源名:唯一名称、默认请求路径,也可以通过
@SentinelResource
的value
属性指定,不可重复。 - 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阀值类型/单机阀值:
QPS(每秒钟的请求数量):当调用该api的QPS达到阀值的时候,进行限流线程数:当调用该api的线程数达到阀值的时候,进行限流 - 是否集群:不需要集群
流控模式:
直接:api达到限流条件时,直接限流
关联:当关联的资源达到阀值时,就限流自己
链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阀值,就限流)【api级别的针对来源】流控效果:
快速失败:直接失败,抛异常
Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阀值/codeFactor,经过预热时长,才达到设置的QPS阀值
排队等待:匀速排队,让请求以均匀的速度通过,阀值类型必须设置为QPS,否则无效
代码展示
基于QPS的流量控制
当QPS
超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:直接拒绝、Warm Up、匀速排队。对应FlowRule
中的 controlBehavior
字段。
在代码中增加以下服务接口
@GetMapping("/testQPSA")
public String testA() {
return "-----------testQPSA";
}
如何此时我们不配置Sentinel,无论如何访问,都不会报错,流量会直接打到服务器,直到服务器崩溃。
此时,我们打开Sentinel,设置流控如下
配置的信息代表每秒最多允许一个请求,超过依次的请求直接报错。
此时我们快速刷新接口,可以看到以下信息
基于并发数的流量控制
基于并发数的控制,也就是控制同一时刻能够访问接口的数量。
@GetMapping("/testThreadA")
public String testThreadA() {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "-----------testThreadA";
}
为了测试方便,我让程序暂停10S。
上图代表我们统一时刻只能有一个线程访问。
然后启动用浏览器及curl
命令同时访问。
可以看到第一个浏览器能正常打开,但是curl
命令的无法访问。
流控模式:直接
以上两个例子都是直接控制,也就是关联自己的资源。
流控模式:关联
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。像对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。
这次我们新增以下两个服务。
@GetMapping("/testRelationA")
public String testRelationA() {
log.info(Thread.currentThread().getName() + "\t testRelationA");
return "-----------testRelationA";
}
@GetMapping("/testRelationB")
public String testRelationB() {
log.info(Thread.currentThread().getName() + "\t testRelationB");
return "-----------testRelationB";
}
我们模拟:当testRelationB
达到阈值时,停止testRelationA
服务。
这次我们用JMeter
进行测试,使用JMeter
循环访问testRelationB
可以看到testRelationB
访问一直正常,但是testRelationA
此时已经不能访问了。
流控模式:链路
只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以限流)
这次我们删除所有的流控规则,查看sentinel我们可以看到/testRelationB
的资源入口是sentinel_web_servlet_context
这次我们设置流控规则如下
多次点击http://localhost:8401/testRelationB
,可以看到浏览器显示
流控效果:快速失败
以上代码都是直接快速失败的例子,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
流控效果:Warm Up(预热)
Warm Up
(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
我们增加以下代码。测试预测
@GetMapping("/testWarmup")
public String testWarmup() {
return "-----------testWarmup";
}
以上表示,一开始系统QPS为1(3/3),10秒之后,系统QPS为3。
启用JMeter测试,设置请求100个,系统处理时间为50s,也就说1s发送两个请求。
可以看到,一开始,间隔成功一个、失败一个(因为我们JMeter设置的是1s处理两个请求,而Sentinel设置的是1s只能处理一个请求),10秒之后,就一直成功了(因为10s后,阈值为10,也就是说我们1s能处理10个请求,小于我们JMeter测试的一秒两个请求)
流控效果:排队等待
排队等待(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
@GetMapping("/testQueue")
public String testQueue() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "-----------testQueue";
}
一次处理一个请求,超时时间为20S,
使用JMeter默认10个请求
可以看到每个线程大概耗时都是1s。
评论 (0)