1.引言
何为热点?热点即经常访问的数据
。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品ID为参数,统计一段时间内最常购买的商品ID并进行限制
- 用户ID为参数,针对一段时间内频繁访问的用户ID进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用LRU
策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
2.热点参数规则
热点参数规则(ParamFlowRule
)类似于流量控制规则(FlowRule
)
属性 | 说明 | 默认值 |
---|---|---|
resource | 资源名,必填 | |
count | 限流阈值,必填 | |
grade | 限流模式 | QPS 模式 |
durationInSec | 统计窗口时间长度(单位为秒),1.6.0 版本开始支持 模式 | 1s |
controlBehavior | 流控效果(支持快速失败和匀速排队模式),1.6.0 版本开始支持 | 快速失败 |
maxQueueingTimeMs | 最大排队等待时长(仅在匀速排队模式生效),1.6.0 版本开始支持 | 0ms |
paramIdx | 热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置 | |
paramFlowItemList | 参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型和字符串类型 | |
clusterMode | 是否是集群参数流控规则 | false |
clusterConfig | 集群流控相关配置 |
3.热点参数限流案例
前面我们已经了解了什么是热点参数限流,下面通过案例说明如何配置以及Sentinel是如何限流的。
【a】控制层添加如下方法
package com.bruce.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @BelongsProject: springcloud-alibaba-nacos
* @BelongsPackage: com.bruce.controller
* @CreateTime: 2021-02-20 17:19
* @Description: TODO
*/
@RestController
@Slf4j
public class HotKeyRuleController {
/**
* @SentinelResource: 指定资源名称,作用与HystrixCommand大体一致
* blockHandler: 指定降级处理方法,类似于Hystrix的fallbackMethod兜底方法
*/
@GetMapping("/testHotKeyRule")
@SentinelResource(value = "testHotKeyRule", blockHandler = "testHotKeyRule_blockHandler")
public String testHotKeyRule(@RequestParam(value = "username", required = false) String username,
@RequestParam(value = "password", required = false) String password) {
return "[热点限流规则]testHotKeyRule..";
}
public String testHotKeyRule_blockHandler(@RequestParam(value = "username", required = false) String username,
@RequestParam(value = "password", required = false) String password,
BlockException exception) {
return "[热点限流规则-兜底方法]testHotKeyRule_blockHandler..";
}
}
重启项目,浏览器访问: http://localhost:8401/testHotKeyRule
说明我们接口是通的,下面我们看看如何配置。
【b】Sentinel热点限流配置,具体配置如下图
上面的配置表示:当访问testHotKeyRule
资源的时候,针对方法testHotKeyRule
的第一个参数【这里对应我们controller中的username参数】的请求,如果一秒内的请求阈值QPS超过1次,那么将会触发兜底方法。
浏览器访问: http://127.0.0.1:8401/testHotKeyRule?username=bruce
注意,这时候我们是一秒内访问一次,可以看到,接口正常返回数据。
突然,我们增加请求接口的频率,手动触发一秒内请求次数大于1次:
可见,当违反上面的配置请求规则时,马上降级处理,成功执行我们指定的blockHandler兜底方法。
下面我们访问:http://localhost:8401/testHotKeyRule?password=123456,注意这个请求并没有包含我们Sentinel配置的参数索引为0的参数【username】,不管我们一秒一次请求还是一秒内疯狂请求资源:
可以看到,当请求接口未包含指定的限流Key时,是不会触发blockHandler兜底方法的。
4.热点限流-参数例外项
前面我们介绍了普通版的热点参数限流,当第一个参数访问次数超过一秒钟一次后,达到阈值1后马上被限流,但我们有时候期望我们的限流参数Key当它是某个特殊值时,它的限流值和平时不一样。所以Sentinel
提供了参数例外项的配置以满足这种特殊需求。
例如:假如前面我们的第一个参数username=bruce的时候,它的阈值可以达到500。
【a】Sentinel参数例外项配置
【b】测试
浏览器疯狂访问:http://localhost:8401/testHotKeyRule?username=bruce123
可以看到,当参数的值不满足例外项配置的值时,默认还是走普通的限流【一秒超过一次就直接降级】。
下面我们测试参数例外项: http://localhost:8401/testHotKeyRule?username=bruce
因为我们前面配置当参数的值等于"bruce"的时候,QPS阈值时500,所以当我们在浏览器一直刷新的时候,一秒钟内基本上不可能超过访问阈值500,所以也就不会触发降级方法。如下图所示:
下面我们说一个需要特别注意的地方,我们在业务方法里面模拟发生异常:
@GetMapping("/testHotKeyRule")
@SentinelResource(value = "testHotKeyRule", blockHandler = "testHotKeyRule_blockHandler")
public String testHotKeyRule(@RequestParam(value = "username", required = false) String username,
@RequestParam(value = "password", required = false) String password) {
int i = 10 / 0;
return "[热点限流规则]testHotKeyRule..";
}
重启项目,浏览器访问:http://localhost:8401/testHotKeyRule?username=bruce,
可以看到,直接将除数为0的报错展示出来了,并不会降级处理, 这里小伙伴们千万别搞错了, blockHandler兜底方法只针对你在Sentinel中配置的限流规则,当你违反了之后才会走兜底降级方法;如果是业务抛出的异常,Sentinel并不会降级处理。
小总结:
@SentinelResource
:处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;
RuntimeException
:java运行时抛出的运行时异常,@SentinelResource不管,@SentinelResource主管配置出错,运行该走异常还是走异常。
5.总结
热点参数限流规则主要是针对请求参数来统计,并实现限流的。首先热点参数是基于QPS统计,如果参数索引设置为0,则以第一个参数统计为准,阈值也是按照基本参数中的阈值来控制的,但是指定的是额外的参数列表的下标,则需要提供指定的热点参数的值,如果当前访问的参数与预设定的参数不一致,依旧与第一个参数的阈值为准。