3. 限流算法基础(二):令牌桶与漏桶

在金融交易系统中,限流算法的选择直接影响交易延迟、资源利用率和业务连续性。上一节我们介绍了计数器与滑动窗口算法,本节将深入讲解两种更精确、更平滑的限流算法——令牌桶(Token Bucket)与漏桶(Leaky Bucket),并分析它们在交易场景中的适用性。

3.1 令牌桶算法

3.1.1 原理与工作流程

令牌桶算法通过一个“桶”来存放令牌,系统以固定速率向桶中添加令牌,每个请求必须从桶中获取一个或多个令牌才能被处理。桶有最大容量,超出部分令牌被丢弃。

  1. 初始化:桶容量为 capacity,初始令牌数为 capacity。
  2. 令牌生成:以速率 rate 个/秒向桶中添加令牌,桶满则停止。
  3. 请求处理:每个请求消耗 n 个令牌(通常 n=1)。若桶中令牌数 ≥ n,则放行并扣除令牌;否则拒绝或排队。
  4. 突发处理:桶中积累的令牌允许短时间内的突发流量,最大突发量为桶容量。
金融场景案例: 某券商行情推送接口允许每秒 1000 次查询,但允许在开盘瞬间 1 秒内处理 2000 次突发请求(桶容量 2000,速率 1000/s),避免瞬时拥堵导致行情延迟。

3.1.2 数学建模

令牌桶的状态可用以下微分方程描述:

令 T(t) 为 t 时刻桶中令牌数,r 为令牌生成速率(个/秒),c 为桶容量。
令牌变化率:dT/dt = r - λ(t)
其中 λ(t) 为 t 时刻的请求消耗速率。
约束条件:0 ≤ T(t) ≤ c
请求放行条件:T(t) ≥ n(n 为请求消耗令牌数)

在离散时间系统中,常用以下更新公式:

# 伪代码:令牌桶状态更新
def token_bucket_update(last_time, current_time, tokens, rate, capacity):
    elapsed = current_time - last_time
    tokens = min(capacity, tokens + rate * elapsed)
    return tokens

3.1.3 令牌桶的典型应用

应用场景 说明 令牌桶优势
交易接口限流 限制每秒下单次数,允许开盘突发 支持突发,平滑长期速率
行情推送控制 控制 WebSocket 推送频率 可配置突发窗口,避免丢包
API 密钥配额 按用户分配不同速率和容量 多桶隔离,粒度细
风控规则引擎 限制单位时间内风控检查次数 允许短时高并发检查
令牌桶算法流程示意图 令牌桶算法流程 令牌生成器 速率 r 个/秒 令牌桶 容量 c 当前令牌数 T(t) 请求处理 T(t) ≥ n ? 放行 ✅ 拒绝 ❌ 关键参数说明: • 令牌生成速率 r:决定长期平均请求速率,如 1000 个/秒 • 桶容量 c:决定最大突发请求量,如 2000 个令牌 • 消耗令牌数 n:每个请求消耗的令牌数,通常为 1,复杂场景可设置不同权重 • 当 T(t) ≥ n 时请求放行,否则拒绝或排队等待

3.2 漏桶算法

3.2.1 原理与工作流程

漏桶算法将请求比作水滴,桶底部有一个固定速率的漏水孔。无论进水速率如何,出水速率始终保持恒定。桶有容量上限,超出部分的水(请求)直接溢出(被拒绝)。

  1. 初始化:桶容量为 capacity,当前水量为 0。
  2. 请求进入:每个请求相当于一滴水,加入桶中。若桶已满,请求被拒绝。
  3. 恒定出水:以固定速率 rate 从桶底漏水,即处理请求。
  4. 平滑输出:无论输入流量如何波动,输出流量始终 ≤ 出水速率。
注意: 漏桶算法强制输出速率恒定,适合需要严格平滑流量的场景,如数据库写入、日志落盘。但在金融交易中,过于严格的平滑可能导致突发交易请求被大量丢弃,造成交易机会损失。

3.2.2 数学建模

漏桶的状态可用以下模型描述:

令 W(t) 为 t 时刻桶中水量,r 为出水速率(请求/秒),c 为桶容量。
水量变化:dW/dt = λ_in(t) - r
其中 λ_in(t) 为输入请求速率。
约束条件:0 ≤ W(t) ≤ c
溢出条件:W(t) + 1 > c 时,新请求被丢弃
出水速率:恒定 r,不受输入影响

离散时间更新公式:

# 伪代码:漏桶状态更新
def leaky_bucket_update(last_time, current_time, water, rate, capacity):
    elapsed = current_time - last_time
    # 先漏水
    water = max(0, water - rate * elapsed)
    return water

3.2.3 漏桶的典型应用

应用场景 说明 漏桶优势
交易日志写入 控制日志落盘速率,防止 IO 打满 输出绝对平滑,保护磁盘
消息队列消费 消费者以固定速率拉取消息 防止消费端过载
数据库写入限流 限制写入 TPS,避免锁竞争 恒定速率,数据库压力可控
监控数据上报 限制上报频率,避免监控系统被打满 流量整形效果好
漏桶算法流程示意图 漏桶算法流程 请求输入 λ_in(t) 可变 漏桶 容量 c 当前水量 W(t) 溢出丢弃 恒定出水 速率 r 恒定(请求/秒) 核心特性: • 输出速率绝对平滑,不受输入波动影响,适合保护后端资源 • 无法利用空闲容量处理突发流量,所有超出桶容量的请求立即丢弃

3.3 令牌桶 vs 漏桶:对比分析

对比维度 令牌桶 漏桶
输出速率 平均速率受控,允许短时突发 绝对恒定,无突发
突发处理 支持,突发量 = 桶容量 不支持,超出即丢弃
空闲利用 可积累令牌,空闲后处理突发 空闲时桶变空,无法积累
适用场景 交易接口、行情推送、API 限流 日志写入、数据库限流、消息消费
实现复杂度 中等,需维护令牌数和时间戳 简单,仅需维护水量和时间戳
金融交易适配性 高,允许开盘突发,符合交易特性 低,过于严格,可能丢失交易机会

3.4 金融交易中的选型建议

实战建议: 在金融 API 限流中,令牌桶是首选算法。它既能控制长期平均速率,又能容忍短时突发流量,完美匹配“开盘瞬间交易量激增、盘中平稳”的典型交易模式。漏桶更适合后端基础设施保护,如数据库写入、日志落盘等需要绝对平滑输出的场景。