4. 令牌桶算法详解:原理、参数配置与API网关实现

令牌桶算法(Token Bucket Algorithm)是API流量整形中最经典、应用最广泛的限流算法之一。它通过一个“桶”来平滑突发流量,同时保证长期平均速率不超过设定阈值,非常适合模型推理API这种既有突发请求、又需要控制整体调用量的场景。

4.1 核心原理

令牌桶算法的核心思想是:系统以恒定速率向一个容量有限的桶中放入令牌,每个请求到达时必须从桶中获取一个或多个令牌才能被处理;如果桶中令牌不足,请求将被排队等待或直接拒绝。

关键机制: 桶的容量决定了允许的突发请求量,令牌放入速率决定了长期平均请求速率。
令牌桶算法核心流程 令牌桶算法核心流程 令牌生成器 速率 r 个/秒 恒定速率 令牌桶 容量 = b(突发容量) 当前令牌数 API 请求 消耗令牌 判断 放行处理 拒绝/等待 💡 算法要点 • 令牌以恒定速率 r 放入桶中,桶满则丢弃多余令牌 • 每个请求消耗 1 个(或多个)令牌,令牌足够则放行,不足则拒绝或排队

4.2 关键参数配置

令牌桶算法有两个核心参数,它们共同决定了流量整形效果:

参数 符号 含义 配置示例 对模型API的影响
令牌放入速率 r 每秒向桶中放入的令牌数量,决定长期平均请求速率 r = 100 令牌/秒 控制模型推理的长期QPS上限,防止过载
桶容量(突发容量) b 桶最多能容纳的令牌数,决定允许的突发请求量 b = 200 令牌 允许短时间内的请求高峰,提升用户体验
参数配置黄金法则: 对于模型推理API,建议将 r 设置为模型单实例能稳定处理的QPS的80%,b 设置为 r 的1.5~3倍,以平衡稳定性和突发处理能力。

4.3 算法工作过程详解

令牌桶算法在每个时间步的执行逻辑可以用以下伪代码精确描述:

// 令牌桶状态
capacity = b          // 桶容量
tokens = b            // 当前令牌数,初始为满
last_time = now()     // 上次更新时间

// 每次请求到达时执行
function allow_request():
    // 1. 计算时间差,补充令牌
    now = current_time()
    elapsed = now - last_time
    tokens = min(capacity, tokens + elapsed * r)
    last_time = now

    // 2. 判断是否放行
    if tokens >= 1:
        tokens = tokens - 1
        return true    // 放行
    else:
        return false   // 拒绝或排队

4.4 在API网关中的实现架构

在实际的API网关(如Kong、APISIX、Nginx + Lua)中,令牌桶算法通常作为插件或中间件实现,部署在模型API的前端。以下是典型的实现架构:

API网关令牌桶实现架构 API网关令牌桶实现架构 客户端 移动端/Web API 网关 路由 令牌桶插件 模型推理服务 GPU/CPU集群 QPS ≤ r 配置中心 r=100, b=200 动态更新 🔧 实现要点 • 令牌桶状态(tokens, last_time)通常存储在共享内存或Redis中,保证分布式一致性 • 配置中心支持动态调整 r 和 b,无需重启网关即可生效

4.5 主流网关的令牌桶实现对比

网关/框架 实现方式 存储后端 动态配置 适用场景
Kong rate-limiting 插件(支持令牌桶模式) 本地内存 / Redis 支持(通过Admin API) 云原生、微服务架构
APISIX limit-req 插件(基于令牌桶) 本地内存 / etcd 支持(热加载) 高性能、Kubernetes集成
Nginx + Lua lua-resty-limit-traffic 库 共享内存(lua_shared_dict) 需自定义 自建网关、高度定制
Spring Cloud Gateway RequestRateLimiter 过滤器 Redis(基于Reactive Redis) 支持(通过配置中心) Java技术栈、Spring生态

4.6 实际配置案例:模型推理API限流

假设你有一个基于GPT的文本生成模型API,单实例稳定处理能力为 50 QPS,允许短时突发到 100 QPS。以下是Kong网关的令牌桶配置示例:

# Kong 声明式配置 (declarative config)
_format_version: "3.0"
services:
  - name: gpt-inference
    host: model-internal.example.com
    port: 8080
    protocol: http
    routes:
      - name: generate-route
        paths:
          - /v1/generate
        methods:
          - POST
    plugins:
      - name: rate-limiting
        config:
          minute: 3000          # 每分钟3000次,对应50 QPS
          hour: 180000
          policy: local         # 使用本地内存
          fault_tolerant: true
          limit_by: consumer    # 按消费者限流
          # 令牌桶模式参数(通过自定义插件或扩展实现)
          # rate: 50            # 令牌放入速率 r = 50 令牌/秒
          # burst: 100          # 桶容量 b = 100 令牌
⚠️ 常见陷阱: 不要将桶容量 b 设置得过大(如 b > 5r),否则会导致长时间的高突发流量压垮后端模型服务。对于模型推理场景,建议 b 不超过 r 的 3 倍,且必须配合后端熔断机制使用。

4.7 令牌桶 vs 漏桶算法

在流量整形领域,令牌桶常与漏桶算法(Leaky Bucket)对比。下表总结了二者的核心差异:

对比维度 令牌桶算法 漏桶算法
处理突发 允许突发(桶容量决定突发量) 强制平滑,不允许突发
输出速率 平均速率 ≤ r,瞬时可高于 r 恒定速率输出,严格等于配置值
适用场景 模型推理API(允许短时高峰) 数据库写入、日志收集(需严格限速)
实现复杂度 中等(需维护时间戳和令牌计数) 较低(队列 + 固定速率消费)
内存开销 低(仅存储令牌数和时间戳) 高(需要请求队列)
选型建议: 对于大多数模型推理API,令牌桶算法是首选。它既能保护后端模型不被过量请求压垮,又能容忍用户侧的合理突发,提供更好的交互体验。如果业务要求绝对的请求速率平滑(如计费系统),则考虑漏桶算法。