上一篇文章,我们一起探讨了Sentinel流量控制的简单用法,本小节,我们一起探讨Sentinel的三种流控规则。
说到系统接口的限流,我们一般会想到用几种限流的算法,比如漏桶算法、令牌桶算法,在单机内,我们可以采用Guava中提供的API帮我们实现限流功能。而在分布式环境中,我们可以用Redis、MQ等中间件来实现限流的功能。
然而,Sentinel为我们提供了更加灵活的限流规则和策略,同时也支持单机和集群模式,方便我们根据不同的应用场景,选择不同的流控、限流的规则。
常用的有如下三种:
-
直接拒绝
-
冷启动+预热
-
匀速+排队
我们可以根据实际需求,来配置相应的流控规则。流控的重要属性如下:
字段 | 说明 | 默认值 |
resource | 资源名,是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS |
limitApp | 流控针对的调用来源 | default,表示不区分调用来源 |
stragety | 调用关系限流策略:直接、链路、关联 | 直连 |
controlBehavior (本文重点讲解这个属性) | 流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流 | 直接拒绝 |
clusterMode | 是否是集群模式 | 否 |
ps:详细的工程创建,请参考笔者的这篇文章 Sentinel:流量控制
直接拒绝
我们都学过Java线程池的拒绝策略,所以通过名字,我们很容易就能想到,如果请求QPS达到了限流的阈值,那么直接将请求丢弃掉。示例代码如下:
public static void CONTROL_BEHAVIOR_DEFAULT(){
String resource = "doSomeThing";
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource(resource);
// QPS模式
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 默认不区分调用分
rule.setLimitApp("default");
// 非集群模式
rule.setClusterMode(false);
// 直接拒绝效果
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
// 限流阈值,允许最多每秒3个请求
rule.setCount(3);
rules.add(rule);
FlowRuleManager.loadRules(rules);
while(true){
if(SphO.entry(resource)){
try{
System.out.println("正常执行");
}finally {
SphO.exit();
}
}else{
System.out.println("被限流,直接拒绝");
}
}
}
根据代码中对限流规则的配置,通过while死循环模拟用户请求,sleep(200)模拟业务的耗时,如果每秒该方法的请求次数超过3次,那么直接将请求丢弃,也就是说,会打印出"被限流,直接拒绝"
我们从代码的执行结果中可以看出,QPS在2-4的范围内波动
正常执行
正常执行
正常执行
正常执行
被限流,直接拒绝
被限流,直接拒绝
正常执行
正常执行
正常执行
被限流,直接拒绝
正常执行
正常执行
被限流,直接拒绝
正常执行
被限流,直接拒绝
正常执行
正常执行
被限流,直接拒绝
正常执行
被限流,直接拒绝
正常执行
冷启动+预热(WarmUp)
当系统长期处于一个低负载的情况下,或服务刚刚启动,这种资源没有加载完成时,如果此时流量压过来,系统很有可能被冲垮。Sentinel也想到了这一点,它为我们提供了冷启动+预热模式,让通过的流量缓慢增加,在一定时间内达到我们配置的阈值上限,达到保护系统的作用。通常冷启动+预热允许通过请求的QPS曲线图如下所示:
示例代码如下
public static void CONTROL_BEHAVIOR_WARM_UP() {
String resource = "doSomeThing";
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource(resource);
// QPS模式
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 冷启动+预热效果
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
// 流控阈值
rule.setCount(4);
// 5s后达到我们配置的阈值上限
rule.setWarmUpPeriodSec(5);
rules.add(rule);
FlowRuleManager.loadRules(rules);
while (true) {
if (SphO.entry(resource)) {
try {
System.out.println("正常执行");
} finally {
SphO.exit();
}
} else {
System.out.println("被限流,直接拒绝");
}
}
}
同样,我们通过while死循环模拟用户请求,将controlBehavior设置为RuleConstant.CONTROL_BEHAVIOR_WARM_UP模式,流控阈值4,warmUpPeriodSec设置为5,表示通过的流量在5秒内缓慢增加,超过的请求直接丢弃,5秒后达到我们设置的阈值上限。
我们从代码运行输出的结果可以看到,系统刚启动时,被拒绝的请求占比较大,大约5s后,趋于稳定。
// 系统刚启动
正常执行
被限流,直接拒绝
被限流,直接拒绝
正常执行
被限流,直接拒绝
被限流,直接拒绝
被限流,直接拒绝
被限流,直接拒绝
正常执行
被限流,直接拒绝
被限流,直接拒绝
被限流,直接拒绝
被限流,直接拒绝
正常执行
// 大约5s后
正常执行
正常执行
正常执行
正常执行
被限流,直接拒绝
正常执行
正常执行
正常执行
正常执行
被限流,直接拒绝
正常执行
匀速+排队
我们可以把匀速+排队,理解为消息队列。流量压过来,把系统能承受范围之外的流量暂时存在消息队列中,在队列的消费方,根据消费方的处理能力缓慢消费,削峰填谷。
Sentinel中的匀速+排队方式会严格控制请求的通过时间,也即是让请求以均匀的速度通过。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。该方式的作用如下图所示:
示例代码如下:
public static void CONTROL_BEHAVIOR_RATE_LIMITER() {
String resource = "doSomeThing";
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource(resource);
// QPS模式
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 匀速+排队效果
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
// 限流阈值,最大每秒2次
rule.setCount(2);
// 最长排队等待时间:20s
rule.setMaxQueueingTimeMs(2 * 1000);
rules.add(rule);
FlowRuleManager.loadRules(rules);
while (true) {
if (SphO.entry(resource)) {
System.out.println("正常执行 " + System.currentTimeMillis());
} else {
System.out.println("被限流,直接拒绝");
}
}
}
为了方便演示,我们把限流阈值设置为2,通过死循环模拟用户请求,我们看输出结果中打印的时间信息,表明大约每秒会通过两个请求,每个请求大约500ms。
正常执行 1593772731264
正常执行 1593772731764
正常执行 1593772732266
正常执行 1593772732768
正常执行 1593772733264
正常执行 1593772733763
正常执行 1593772734262
正常执行 1593772734764
正常执行 1593772735264
正常执行 1593772735764
正常执行 1593772736267
另外,需要注意一点就是,要是匀速+排队效果生效的话,规则的限流类型一定是RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER。否则该效果不会生效。
好了,今天一起探讨的内容相对比较简单,从三个限流效果为大家演示了Sentinel强大的作用,为我们的系统保驾护航。在接下来的文章中,我会为大家一起探讨Sentinel更多的玩法,谢谢大家。