八、Sentinel之流量控制
FlowSlot 会根据预设的规则,结合前面 NodeSelectorSlot、ClusterNodeBuilderSlot、StatistcSlot 统计出来的实时信息进行流量控制。
限流的直接表现是在执行 Entry nodeA = SphU.entry(资源名字) 的时候抛出 FlowException 异常。FlowException 是 BlockException 的子类,您可以捕捉 BlockException 来自定义被限流之后的处理逻辑。
同一个资源可以对应多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
-
resource:资源名,即限流规则的作用对象
-
count: 限流阈值
-
grade: 限流阈值类型,QPS 或线程数
-
strategy: 根据调用关系选择策略
流量控制主要有两种统计类型,一种是统计线程数,另外一种则是统计 QPS。
一、 流控类型
QPS流量控制
QPS是每秒请求数,QPS流量控制是请求数流量控制,当每秒的请求数超过设置的阈值时触发流量控制。
并发线程数流量控制
线程数限流用于保护业务线程数不被耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对高线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离),或者使用信号量来控制同时请求的个数(信号量隔离)。这种隔离方案虽然能够控制线程数量,但无法控制请求排队时间。当请求过多时排队也是无益的,直接拒绝能够迅速降低系统压力。Sentinel线程数限流不负责创建和管理线程池,而是简单统计当前请求上下文的线程个数,如果超出阈值,新的请求会被立即拒绝。
二、流控模式
点开流控规则的高级选项,可以看到有三种流控模式:
流控模式 | 说明 |
---|---|
直接 | 对当前请求的资源达到阈值触发限流,默认 |
关联 | 与当前请求的资源关联的另一个资源达到阈值时,触发当前资源的限流 |
链路 | 从指定链路访问本资源,达到阈值时触发限流 |
在Sentinel的左侧选择服务实例后,选择流控规则
,点击右上角的新增流控规则
,点击高级选项
:
资源名如下:
@RequestMapping("/index")
@SentinelResource("HelloConsumer-index")
public String index() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "index";
}
直接模式
设置HelloConsumer-index
资源的QPS为20,用jmeter测试:
看到异常为25%
。当资源HelloConsumer-index
的QPS超过20时触发限流。
关联模式
资源名:
@RequestMapping("/write")
@SentinelResource("HelloConsumer-write")
public String write() {
return "success";
}
@RequestMapping("/read")
@SentinelResource("HelloConsumer-read")
public String read() {
return "success";
}
HelloConsumer-read关联HelloConsumer-write:
用jmeter测试:
运行jmeter后,用浏览器访问http://localhost:7001/consumer/read,发现被限流。关闭jemter后在访问http://localhost:7001/consumer/read,发现可以访问。
链路模式
要使用链路模式,首先关闭Context收敛,这样被监控方法可以进行不同链路的单独控制,在application.properties配置:
spring.cloud.sentinel.web-context-unify=false
添加公共资源:
@Service
public class HelloService {
@SentinelResource("HelloService")
public String hello() {
return "hello service";
}
}
添加两个链路入口:
@RequestMapping("/consumer/hello1")
public String hello1() {
return helloService.hello();
}
@RequestMapping("/consumer/hello2")
public String hello2() {
return helloService.hello();
}
在Sentinel中配置限流规则:
只有从/consumer/hello1
进入访问资源HelloService
才限流。
接下来配置jmeter:
让jmeter持续执行。用浏览器访问http://localhost:7001/consumer/hello1,看到发生了限流。访问http://localhost:7001/consumer/hello2,发现没有限流。停止jmeter后访问http://localhost:7001/consumer/hello1,发现没有限流。
三、流控效果
只有选择阈值类型为QPS时才有流控效果:
流控效果 | 说明 |
---|---|
快速失败 | 该方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。 |
Warm Up(预热) | 该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。 |
排队等待 | 让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长,对应的是漏桶算法。 |
Warm Up
在Sentinel配置流控效果:
这里配置预热时长是5,在5秒后系统接收请求数逐步达到最大阈值。
在jmeter中配置:
配置的最大QPS是20。
执行后,查看结果:
可以看到,随着时间的增加,通过的请求数增多,拒绝的请求数减少,最后全部通过。
排队等待
这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
在Sentinel中配置排队等待流控效果:
超时时间设置为5秒。
配置jmeter:
配置QPS为15。
通过实时监控:
通过的QPS都是10。不管发出的流量是多少,输出的QPS都是10。起了均衡流量的作用。