return allowed != null && allowed == 1L;
}
```
工程要点:
- **多维度限流**:活动维度 + 用户维度 + IP/设备维度(防羊毛)
- **错误策略**:Redis 超时/抖动时,宁可拒绝也不要“放行全部”(否则下游暴毙)
- **观测**:把 `allowed/blocked` 打到 metrics(后面第 7 篇会展开)
### 4.2 “强公平”要另外做:令牌桶只保证速率,不保证排队
我们做过一个反直觉实验:
- 令牌桶 rate=2000/s
- 10 台应用实例
- 1000 个用户同时开始请求
理论上每个人都有机会。
现实:
- RTT 更小、重试更激进的客户端更容易拿到令牌
- 一些用户在 3 秒内连续拿到多个令牌
所以结论是:
> 令牌桶解决“系统能力边界”,不负责“用户体验公平”。
公平,要靠“排队”。
### 4.3 细粒度限流:别只按活动控流,还要按用户/渠道控流
活动维度限流解决的是“系统整体别被打穿”;但秒杀现场真正把你拖死的,往往是两种请求:
- **机器人/脚本的高频重试**:一秒几十次,拿不到也不走
- **边缘网络抖动导致的重复提交**:客户端以为失败,实际上你已经在处理
所以我们会叠加两层“人类友好”的限流:
1) **用户维度**:`rl:user:{userId}:{activityId}`,例如 1 秒最多 2 次(拿不到就提示排队,别鼓励狂点)
2) **渠道维度**:对特定渠道(小程序/APP/H5)设置不同的桶容量,避免某个渠道异常把所有人拖下水
如果你在面试里想讲得更工程一点,可以补一句:
> “活动维度是保护系统能力,用户维度是保护用户体验,渠道维度是保护运营策略。”
### 4.4 漏桶 vs 令牌桶:我们为什么更偏爱令牌桶
- 漏桶更像“匀速出水”,**突发几乎不允许**,延迟更可控,但用户峰值体验更差
- 令牌桶允许“存量令牌”吸收小突发,**更适合秒杀这种‘到点起跑’的业务**
但注意:令牌桶的“突发”不是免费的,它只是把压力从“那一秒”挪到“接下来的几秒”。
如果你桶设得太大,下游还是会被冲击,所以我们通常会把容量设为 `rate * 1~2s` 的量级。
### 4.5 限流不是拍脑袋:至少把三个数算清楚
落地时我会逼自己(也逼产品)回答三个问题:
- **下游硬能力**:库存服务、订单落库、MQ 投递,各自的稳定吞吐是多少?(别拿