文章目录
1. 前言
在前面的文章中,已经介绍过了 Sentinel安装和基本使用。这次主要讲的是Sentinel 的流量控制规则,使用版本为1.8.0,它提供了以下几个配置项:
- 资源名: 唯一名称,默认是接口的请求路径
- 针对来源: Sentinel可以针对调用者限流,填写微服务名称,默认default(不区分来源)
- 阈值类型/单机阈值:
- QPS:当调用该API的QPS达到阈值,进行限流
- 线程数:当调用该API的线程数达到阈值,进行限流
- 是否集群: 不需要
- 流控模式:
- 直接:该API达到限流条件,进行限流
- 关联:当关联的资源达到阈值,就限流自己
- 链路:只记录指定链路上的流量,指定资源从入口资源进来的流量,如果达到条件,就进行限流(API级别的针对来源)
- 流控效果:
- 快速失败:直接失败,抛出异常
- Warm Up:根据codeFactor(冷加载因子,默认3),从 阈值/codeFactor 开始,经过配置的预热时长,才达到设定的QPS阈值
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
2. 阈值类型
2.1 QPS
Queries Per Second,每秒请求量。
首先,我们给 /test
接口配置如下流控规则。
配置项 | 值 |
---|---|
QPS | 10 |
流控规则 | 直接 |
流控效果 | 快速失败 |
接着,启动JMeter在1秒内发送20个请求。结果就是产生大量异常,直接报错Blocked by Sentinel (flow limiting)
,说明Sentinel拦截生效
2.2 线程数
将上一步的 QPS 改成 线程数,阈值设置为2,测试结果如下:
竟然所有请求都通过了,这是因为CPU的执行速度远远超出了我们的想象,并非每个HTTP请求都会New出新的线程进行执行,可能一个线程就全部处理掉了。为了让CPU创建大量线程,我们改下接口代码:
@GetMapping("/test")
public String test(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "test";
}
重新执行JMeter,结果如下,符合预期:
3. 流控模式
3.1 直接
针对当前资源的接口进行控制,前面的例子用的就是这项配置
3.2 关联
当关联的资源达到阈值,就限流自己。例如:当支付接口的调用达到阈值,就限制下单接口。
创建另一个接口 /test2
,针对/test
资源配置如下限流规则:
接下来,使用JMeter对/test2
接口发起大量请求。然后,立刻马上在浏览器访问/test
,就能重现Blocked by Sentinel (flow limiting)
的异常。这就是关联流控的使用方式。
3.3 链路
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流;它的功能有点类似于针对 来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细.
举个例子:
创建一个OrderService,里面写个getOrder方法,并加上 @SentinelResource 注解,表示当前方法资源名称是 getOrder
@Service
public class OrderServiceImpl implements OrderService {
@SentinelResource(value = "getOrder")
@Override
public String getOrder(String id) {
return "Order:" + id;
}
}
在controller分别创建2个接口:order1、order2
@GetMapping("/order1")
public String order1(){
return orderService.getOrder("111");
}
@GetMapping("/order2")
public String order2(){
return orderService.getOrder("222");
}
打开sentinel控制台,对getOrder资源进行流控规则设置。
入口资源设置为 /order1,表示从/order1接口调用才进行流控,如果从/order2调用的不做任何控制
在浏览器反复调用 http://localhost:8020/order1 ,发现流控没生效 - -!
这是因为从1.6.3 版本开始, Sentinel Web filter默认收敛所有URL的入口context
1.7.0 版本开始(对应Spring Cloud Alibaba的2.1.1.RELEASE),官方在引入了spring.cloud.sentinel.web-context-unify 参数,用于控制是否收敛context;将其配置为 false 即可根据不同的URL 进行链路限流。
官方源码如下:
如上图所示:web-context-unify 为true的时候,取到的是收敛的context名称sentinel_spring_web_context;为false的时候,就可以正确地取到 /order1 资源名。
在 application.yml 修改参数之后,再次测试,就能正常进行限流了
4. 流控效果
4.1 快速失败
抛出Blocked by Sentinel (flow limiting)
异常,前面的例子用的就是这项配置
4.2 Warm Up
预热、冷启动
如下图,系统将从 10/冷却因子3 开始,经过10秒时间缓慢将阈值升到单机阈值10
配置JMeter,在30秒内发送300次请求,也就是10次/秒。 测试结果如下:
一开始的请求,存在大量报错,因为这时候阈值从3开始,还没达到10
到最后,阈值达到10了就不再报错
4.3 排队等待
严格地控制请求通过的间隔时间,也就是让请求匀速地通过,对应的是漏桶算法。这种方式适合用于请求以突刺状来到,这个时候我们不希望一下子把所有的请求都通过,这样可能会把系统压垮;同时我们也期待系统以稳定的速度,逐步处理这些请求,以起到“削峰填谷”的效果,而不是拒绝所有请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
- 官方手册:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
- 源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
测试:设置QPS=1,也就是每秒处理1个请求,剩下的排队等待,5秒后超时
使用JMeter每秒发送2次请求
从第6秒开始,就开始出现超时异常