普通QPS限流无法应对秒杀热点,因全局限流无法区分商品ID;需用热点参数限流,显式指定参数索引、类型及例外项阈值,并确保TokenServer集群部署与参数序列化一致。

为什么普通 QPS 限流在秒杀里根本挡不住热点商品
因为普通 FlowRule 是对整个接口统一限流,比如 /products/{id} 设置 QPS=100,那不管 id=1 还是 id=999,加起来不能超 100。但秒杀时,99% 请求都打向 id=123(爆款),其余商品几乎没人点——结果是爆款直接被打穿,其他商品却“饿着”。热点参数限流才能让 id=123 单独受控,而 id=456 照常放行。
必须显式声明参数索引和类型,否则规则不生效
Sentinel 不会自动解析路径变量或 JSON body,你得告诉它“我要取第几个参数、它是什么类型”。比如 Spring MVC 接口定义为 public Result detail(@PathVariable Long id),实际传入的是 long 型,但 Sentinel 默认按 Object 处理,会导致匹配失败。常见错误现象:规则已加载,但 id=123 频繁触发却从不限流。
- 用
setParamIdx(0)指定第一个参数(注意不是路径里的位置,是方法签名中参数顺序) - 必须调用
setClassType(Long.class.getName())或String.class.getName(),不能省略 - 如果参数是包装类(如
Long),不要写成"long";原始类型才用"long" - Spring Cloud Alibaba 2.2.10+ 支持自动提取
@PathVariable和@RequestParam,但仍需显式指定索引和类型
例外项(ParamFlowItem)才是秒杀场景的关键配置点
全量商品不能一视同仁:秒杀商品需要更高阈值(比如 QPS=50),普通商品保持 QPS=5。这靠 ParamFlowItem 实现,而不是新建多条规则。容易踩的坑是把例外项当成“白名单”——它其实是“针对特定值的独立限流策略”,仍属限流范畴,只是阈值不同。
示例:给商品 ID 123 单独设置每秒最多 50 次请求,其余 ID 共享默认阈值 5:
ParamFlowRule rule = new ParamFlowRule("product:detail")
.setParamIdx(0)
.setCount(5) // 默认阈值
.setParamFlowItemList(Arrays.asList(
new ParamFlowItem()
.setObject("123") // 注意:这里必须是字符串形式,即使参数是 Long
.setClassType(Long.class.getName())
.setCount(50)
));
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
-
setObject("123")必须是字符串,Sentinel 内部用toString()匹配 - 多个例外项用
Arrays.asList()传入,不是多次调用loadRules - 例外项不支持通配符或正则,只能精确匹配值
集群模式下热点统计不一致?检查 TokenServer 部署和参数序列化
单机模式下热点识别正常,上集群后 id=123 的统计值忽高忽低,甚至完全不触发限流——大概率是 TokenServer 未部署,或客户端和服务端参数序列化方式不一致。Sentinel 热点统计依赖 TokenServer 统一聚合,否则各节点各自为政,无法识别跨实例的“真热点”。
- 必须部署独立的
sentinel-dashboard并启用TokenServer(启动参数加-Dproject.name=your-service) - 确保所有服务节点使用相同版本的
sentinel-parameter-flow-control依赖 - 如果参数是自定义对象(如
ProductQueryDTO),必须实现toString()返回可区分的标识,否则所有对象都算作同一个热点值 - 集群限流效果有 1–2 秒延迟,压测时别只看首秒数据
真正难的是让热点识别足够快又足够准:窗口太短(如 1 秒)容易误判临时抖动,太长(如 60 秒)又来不及拦截秒杀洪峰。线上建议从 5 秒滑动窗口起步,再根据监控曲线微调。


















