连载中 4/20

漏桶与令牌桶:匀速与突发的两种哲学

2026-07-07 · 1533 阅读 · 0 评论 · 0 赞

从「数历史」到「控节奏」

窗口类算法的内在逻辑是事后统计:来一个记一个,回头看账本超没超。本篇的两位主角换了一套内在逻辑——事前控制:不是数你来了多少,而是规定「通行证以什么节奏发放」,拿到通行证才有资格过。节奏可控,流量形状就可控。两位主角用两个比喻讲清:一个漏桶,一个令牌桶。

漏桶:把水剁成水滴

漏桶的形象:桶口任意倒水(流量任意突进),桶底一个小孔恒定漏水(恒定速率放行)。水倒得太快,桶满了就溢出——溢出的就是被拒绝的请求。它的关键词是绝对匀速:不管入口多狂暴,出口永远 100 毫秒一滴。工程实现是队列加定时消费:请求进 FIFO 队列排队,消费线程按固定速率取用,队列即桶身,容量即缓冲上限。

这套强项叫流量整形(Shaping):下游只见到平滑如镜的匀速流量,特别适合保护「消化能力固定」的第三方——短信通道、支付网关、慢速的外部接口。代价同样鲜明:排队延迟。突发 100 个合法请求,在漏桶里要等 10 秒才能全部放行,用户等不到;而且桶是死的,攒不了额度——平时闲着,来了突发也不让快走。Nginx 的 limit_req 模块正是漏桶思想的实现。

令牌桶:匀速发牌,攒着打突发

令牌桶倒过来想:系统按恒定速率往桶里放令牌(比如每秒 200 枚),桶有容量上限;请求来了先取令牌,取到就过,取不到就拒。平时请求稀疏,令牌在桶里攒着;突发来临,攒下的额度允许瞬间放行一批——匀速发牌、按需消费,突发的灵活性就出来了。这是流量控制(Rate Control):长期平均速率被锁死,短期突发被桶容量允许。

// 令牌桶核心逻辑(惰性补充版)
private double tokens = capacity;                 // 当前令牌数
private long lastRefill = System.nanoTime();

public synchronized boolean tryAcquire(int n) {
    long now = System.nanoTime();
    // 惰性补充:不搞定时线程,每次来按时间差补发令牌
    tokens = Math.min(capacity, tokens + (now - lastRefill) / 1e9 * rate);
    lastRefill = now;
    if (tokens >= n) { tokens -= n; return true; }
    return false;                                  // 令牌不够,拒绝
}

注意「惰性补充」这个细节:不需要定时线程按速率发牌,只需记住上次补充时间,请求到来时按「经过的时间 × 速率」一次性补齐——省线程、省锁竞争,公式一行搞定。Guava 的 RateLimiter 用的正是这套思想。

Guava RateLimiter:单机令牌桶的标准答案

RateLimiter limiter = RateLimiter.create(200);    // 每秒 200 个令牌
limiter.acquire();                                // 阻塞式:等不到令牌就睡
if (limiter.tryAcquire(100, TimeUnit.MILLISECONDS)) {
    // 限时抢令牌:抢到干活,抢不到走降级
} else {
    // 降级路径
}

两个变体值得一提:SmoothBursty 默认攒 1 秒的突发额度;SmoothWarmingUp 带预热——刚启动时发牌慢,随时间逐步提到全速,专门治「重启风暴」(第 1 篇第四幕:重启后积压流量打垮冷系统),缓存未热、连接池未满的冷系统最怕一上来就全速。

一队各表

维度漏桶令牌桶
核心思想恒定速率放行,多余的排队或丢弃恒定速率发牌,凭牌通行
突发处理不允许,强行排队匀速化允许,桶内存量即突发额度
延迟特征有排队延迟有牌即过,无排队
定位流量整形,保护固定消化力的下游流量控制,约束平均速率
代表实现Nginx limit_reqGuava RateLimiter、Sentinel 排队模式

选型口诀:护第三方用漏桶,管自己用令牌桶——短信网关、支付通道这类消化力刚性的下游,漏桶强制匀速最体贴;自家接口的容量保护,令牌桶的突发宽容更合理。下一篇把令牌桶真正落到生产代码:单机注解式限流的完整姿势,顺带回答「限流怎么优雅地融入业务代码」。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#漏桶#令牌桶#Guava#RateLimiter#流量整形#Nginx

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞