3. 限流算法基础(二):令牌桶与漏桶
在金融交易系统中,限流算法的选择直接影响交易延迟、资源利用率和业务连续性。上一节我们介绍了计数器与滑动窗口算法,本节将深入讲解两种更精确、更平滑的限流算法——令牌桶(Token Bucket)与漏桶(Leaky Bucket),并分析它们在交易场景中的适用性。
3.1 令牌桶算法
3.1.1 原理与工作流程
令牌桶算法通过一个“桶”来存放令牌,系统以固定速率向桶中添加令牌,每个请求必须从桶中获取一个或多个令牌才能被处理。桶有最大容量,超出部分令牌被丢弃。
- 初始化:桶容量为
capacity,初始令牌数为capacity。 - 令牌生成:以速率
rate个/秒向桶中添加令牌,桶满则停止。 - 请求处理:每个请求消耗
n个令牌(通常 n=1)。若桶中令牌数 ≥ n,则放行并扣除令牌;否则拒绝或排队。 - 突发处理:桶中积累的令牌允许短时间内的突发流量,最大突发量为桶容量。
金融场景案例: 某券商行情推送接口允许每秒 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 密钥配额 | 按用户分配不同速率和容量 | 多桶隔离,粒度细 |
| 风控规则引擎 | 限制单位时间内风控检查次数 | 允许短时高并发检查 |
3.2 漏桶算法
3.2.1 原理与工作流程
漏桶算法将请求比作水滴,桶底部有一个固定速率的漏水孔。无论进水速率如何,出水速率始终保持恒定。桶有容量上限,超出部分的水(请求)直接溢出(被拒绝)。
- 初始化:桶容量为
capacity,当前水量为 0。 - 请求进入:每个请求相当于一滴水,加入桶中。若桶已满,请求被拒绝。
- 恒定出水:以固定速率
rate从桶底漏水,即处理请求。 - 平滑输出:无论输入流量如何波动,输出流量始终 ≤ 出水速率。
注意: 漏桶算法强制输出速率恒定,适合需要严格平滑流量的场景,如数据库写入、日志落盘。但在金融交易中,过于严格的平滑可能导致突发交易请求被大量丢弃,造成交易机会损失。
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,避免锁竞争 | 恒定速率,数据库压力可控 |
| 监控数据上报 | 限制上报频率,避免监控系统被打满 | 流量整形效果好 |
3.3 令牌桶 vs 漏桶:对比分析
| 对比维度 | 令牌桶 | 漏桶 |
|---|---|---|
| 输出速率 | 平均速率受控,允许短时突发 | 绝对恒定,无突发 |
| 突发处理 | 支持,突发量 = 桶容量 | 不支持,超出即丢弃 |
| 空闲利用 | 可积累令牌,空闲后处理突发 | 空闲时桶变空,无法积累 |
| 适用场景 | 交易接口、行情推送、API 限流 | 日志写入、数据库限流、消息消费 |
| 实现复杂度 | 中等,需维护令牌数和时间戳 | 简单,仅需维护水量和时间戳 |
| 金融交易适配性 | 高,允许开盘突发,符合交易特性 | 低,过于严格,可能丢失交易机会 |
3.4 金融交易中的选型建议
实战建议: 在金融 API 限流中,令牌桶是首选算法。它既能控制长期平均速率,又能容忍短时突发流量,完美匹配“开盘瞬间交易量激增、盘中平稳”的典型交易模式。漏桶更适合后端基础设施保护,如数据库写入、日志落盘等需要绝对平滑输出的场景。