漏桶算法适合硬限流但无突发弹性,令牌桶更适web api且支持短时突发;生产限流应优先选redis实现的令牌桶,并注意key分桶、中间件顺序及超时熔断配合。

漏桶算法在 Python 中适合做硬限流,但别用它防突发流量
漏桶本质是恒定速率出水,time.sleep() 或固定间隔 deque.popleft() 都能模拟,但它对突发请求毫无弹性——哪怕系统空闲,请求也得排队等“漏水”。适合下游服务极其脆弱、必须严格控速的场景,比如调用银行接口或写入硬件设备。
- 常见错误:用漏桶应对秒杀流量,结果大量请求在桶里堆积超时,
TimeoutError暴增 - 真实使用场景:定时同步日志到 ELK,每秒最多发 10 条,多一条都不行
- 性能影响:桶容量大时,内存占用线性增长;用
threading.Lock保护桶状态会成瓶颈
令牌桶更适合 Web API 限流,Python 用 redis-py 实现最稳
令牌桶允许短时突发(桶里有余量就放行),更贴合真实业务。纯内存实现(如 threading.local)只适用于单进程调试;生产必须依赖 Redis,靠 INCR + EXPIRE 原子操作保一致性。
- 参数关键点:
rate(令牌生成速率)、capacity(桶大小)要分开调:高并发接口可设capacity=100+rate=20/second,避免冷启动时全拒 - 容易踩的坑:没设
EXPIRE导致桶状态永久残留;用float(time.time())算令牌数引发浮点误差,建议用整数时间戳 +int()截断 - 兼容性注意:Redis Cluster 下
EVAL脚本需确保 key 在同一 slot,否则报CROSSSLOT错误
ratelimit 和 slowapi 这类库默认用令牌桶,但别直接信文档里的“开箱即用”
它们封装了逻辑,但默认配置常忽略业务细节。比如 ratelimit 的 @rate_limit 装饰器,默认用内存存储,部署多实例就失效;slowapi 的 Limiter 若不显式传 key_func,所有用户共用一个桶。
- 实操建议:强制指定
key_func,例如按request.client.host或request.headers.get("X-User-ID")分桶 - 检查中间件顺序:FastAPI 中若
Limiter在CORSMiddleware后注册,预检请求(OPTIONS)可能被误限 - 错误现象:
429 Too Many Requests返回但响应头没带X-RateLimit-Remaining,说明底层没正确注入响应头逻辑
业务决策不是选算法,而是看“谁承担溢出成本”
漏桶让请求方等,令牌桶让请求方抢。如果超限后重试成本低(比如客户端自动轮询),用令牌桶;如果超限意味着订单丢失或资金风险(如支付回调),就得用漏桶+明确拒绝+返回 429 + 重试建议头。
立即学习“Python免费学习笔记(深入)”;
- 最容易被忽略的一点:两种桶都解决不了“慢请求拖垮系统”的问题——限流只管数量,不管单个请求耗时。得配合
timeout参数或熔断器(如tenacity)一起用 - 复杂点在于混合策略:核心接口用漏桶保底,非核心用令牌桶保体验,这时得在网关层统一鉴权和分流,而不是在每个服务里各自实现










