限流:令牌桶与漏桶的实际差别

令牌桶允许突发,漏桶把流量削成恒定速率。选错的典型症状是正常用户被限、或者限流形同虚设,取决于你的下游能不能吃突发。

限流的两个经典模型常被当成同义词,但它们的输出形状完全不同:一个保留突发,一个削平突发。

令牌桶:允许突发

桶里按固定速率累积令牌,容量为 C。请求来了取一个令牌,取不到就限流。

  • 速率 R 决定长期平均吞吐
  • 容量 C 决定能容忍多大的突发

空闲一段时间后桶会积满,此时可以瞬时放行 C 个请求。这对「页面加载同时发 20 个请求」这类正常行为是必要的 —— 漏桶会把它们摊平,用户看到的是逐条加载。

漏桶:恒定输出

请求先进队列,再以固定速率流出。队列满则丢弃。

输出速率恒定,代价是引入了延迟:请求必须在队列里等待。对下游保护更彻底,但用户体验会退化。

对照

维度 令牌桶 漏桶
输出速率 可变,最高到 C 恒定
是否允许突发 允许 不允许
引入延迟 否 是
适合保护 自己有弹性的服务 脆弱的下游

该选哪个

问一个问题:下游能不能消化突发?

场景 选择 理由
用户 API 网关 令牌桶 正常用法就有突发
调用第三方付费接口 漏桶 对方按秒计费,突发无意义
保护数据库连接池 漏桶 突发会耗尽连接
单用户频率限制 令牌桶 不该惩罚短时密集操作

分布式下的坑

单机内存计数在多实例部署下等于把限额乘以实例数。要么用共享存储(Redis 的 INCR + 过期),要么给每个实例分配 限额 / 实例数。

Redis 方案要注意原子性:GET 再 SET 会漏掉并发。用 INCR 配合 EXPIRE,或者一段 Lua 脚本一次完成。

返回什么状态码

被限流时返回 429 Too Many Requests,并带上 Retry-After 告诉客户端等多久。不带 Retry-After 的 429 会让客户端盲目重试,等于自己放大流量。

令牌桶管的是「长期平均 + 突发预算」,漏桶管的是「常量输出」。先确定下游能吃哪种,再选模型。

← 返回文章列表

评论

…