4. 常见限流算法详解(下):令牌桶算法、漏桶算法
在上一节中,我们深入剖析了计数器算法与滑动窗口算法的实现原理与边界问题。本节将聚焦于两种在金融低延迟系统中应用最广泛的“桶”模型算法——令牌桶算法与漏桶算法。理解它们在应对突发流量与平滑流量时的本质差异,是设计高可用限流架构的关键。
4.1 令牌桶算法(Token Bucket)
令牌桶算法允许一定程度的突发流量,同时保证长期的平均速率。其核心思想是:系统以恒定速率向一个桶中放入令牌,每个请求必须从桶中获取一个或多个令牌才能被处理。如果桶中没有足够的令牌,请求将被拒绝或等待。
4.1.1 核心参数与工作原理
| 参数 | 说明 | 典型值(金融场景) |
|---|---|---|
| 桶容量(burst) | 桶中最多能存放的令牌数,决定了允许的最大突发流量 | 1000 ~ 10000 |
| 令牌生成速率(rate) | 每秒钟向桶中添加的令牌数量,决定了长期平均速率 | 100 ~ 5000 QPS |
| 当前令牌数(tokens) | 桶中实时剩余的令牌数量 | 动态变化 |
| 最后填充时间(lastRefillTime) | 上次计算令牌数量的时间戳,用于计算应补充的令牌 | 毫秒级时间戳 |
算法流程如下:
- 令牌补充:每次请求到达时,根据当前时间与上次补充时间的差值,计算应补充的令牌数(
delta = (now - lastRefillTime) * rate / 1000),并将令牌数更新为min(capacity, tokens + delta)。 - 令牌消耗:如果当前令牌数 ≥ 请求所需令牌数(通常为1),则消耗对应令牌,允许请求通过;否则拒绝请求。
- 突发处理:当桶满时,多余的令牌被丢弃,但桶中已有的令牌可以支撑瞬时高流量。
性能关键点:令牌桶算法的核心操作是“时间差计算 + 令牌数更新”,时间复杂度为 O(1)。在金融低延迟场景中,推荐使用 预计算 + 懒更新 策略,即不在定时器中补充令牌,而是在每次请求时按时间差批量补充,避免额外的线程调度开销。
4.1.2 令牌桶算法流程图
4.2 漏桶算法(Leaky Bucket)
漏桶算法与令牌桶算法相反,它强制一个恒定的输出速率,不允许突发。其核心思想是:将请求看作水滴,桶底部有一个固定大小的漏洞,水以恒定速率流出。当桶满时,新进入的水滴会溢出(即请求被拒绝)。
4.2.1 核心参数与工作原理
| 参数 | 说明 | 典型值(金融场景) |
|---|---|---|
| 桶容量(capacity) | 桶中最多能容纳的请求数,相当于等待队列长度 | 100 ~ 500 |
| 漏出速率(leakRate) | 每秒钟从桶中流出的请求数,即处理速率 | 100 ~ 5000 QPS |
| 当前水量(water) | 桶中当前积压的请求数 | 动态变化 |
| 上次漏水时间(lastLeakTime) | 上次计算漏水量的时间戳 | 毫秒级时间戳 |
算法流程如下:
- 漏水计算:每次请求到达时,根据当前时间与上次漏水时间的差值,计算应漏出的水量(
delta = (now - lastLeakTime) * leakRate / 1000),并将水量更新为max(0, water - delta)。 - 入桶判断:如果当前水量 + 1 ≤ 桶容量,则允许请求入桶(水量+1),请求被排队等待处理;否则请求被拒绝。
- 恒定输出:无论入桶速率如何,出桶速率始终为
leakRate,实现绝对平滑。
注意:漏桶算法在金融系统中常用于保护下游数据库或第三方接口,这些场景对请求速率的波动极其敏感。但漏桶算法会引入额外的排队延迟,不适合对延迟要求极高的交易链路。
4.2.2 漏桶算法流程图
4.3 突发流量 vs 平滑流量:性能差异对比
两种算法在应对不同流量模式时表现出截然不同的特性。下表从多个维度进行对比:
| 对比维度 | 令牌桶算法 | 漏桶算法 |
|---|---|---|
| 突发流量处理 | 允许突发,桶中积攒的令牌可一次性消耗 | 不允许突发,输出速率恒定 |
| 平滑流量处理 | 长期平均速率受控,但瞬时速率可能波动 | 输出绝对平滑,无任何波动 |
| 延迟特性 | 低延迟,令牌足够时请求立即通过 | 引入排队延迟,延迟与当前水量成正比 |
| 资源利用率 | 较高,可充分利用空闲时段处理突发 | 较低,即使系统空闲也按恒定速率处理 |
| 适用场景 | API网关、交易系统、需要应对秒杀/抢购的场景 | 数据库保护、第三方接口限流、对抖动敏感的下游 |
| 实现复杂度 | 中等,需处理令牌补充与消耗的原子性 | 中等,需处理漏水计算与入桶判断 |
4.3.1 流量模式对比图
4.4 金融场景选型建议
- 交易核心链路(订单、支付、风控):推荐使用 令牌桶算法。这些场景需要应对秒杀、行情波动等突发流量,同时要求低延迟。令牌桶可以在系统空闲时积攒令牌,在突发时快速释放,最大化资源利用率。
- 数据库/缓存保护:推荐使用 漏桶算法。数据库连接池、Redis 实例等对请求速率波动非常敏感,漏桶的恒定输出特性可以有效防止“惊群效应”和连接池打满。
- 第三方接口限流:根据第三方接口的 SLA 选择。如果第三方允许突发,使用令牌桶;如果第三方强制 QPS 上限且不接受突发,使用漏桶。
实战经验:在金融系统中,我们通常将令牌桶算法用于 API 网关层,将漏桶算法用于 服务间调用层。网关层需要快速响应突发流量,而服务间调用需要保护下游资源。两种算法可以组合使用,形成多层限流架构。