连载中 4/20

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

2026-07-07 · 1530 阅读 · 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)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞