2. 常见限流算法:原理与对比

在低延迟模型 API 的限流熔断设计中,限流算法是核心决策点。不同的算法在突发处理、平滑度、内存开销和实现复杂度上差异显著。本节深入剖析四种主流限流算法的原理、适用场景与性能特征。

2.1 计数器算法(Counter Algorithm)

计数器算法是最直观的限流实现。它将时间划分为固定窗口(如 1 秒),在每个窗口内维护一个计数器,请求到达时计数器加 1;当计数器达到阈值时,拒绝后续请求;窗口结束时重置计数器。

核心特征:实现简单,内存开销极低(仅需一个计数器和时间戳),但存在严重的“窗口边界突发”问题——在窗口切换瞬间可能允许两倍于阈值的请求通过。
// 计数器算法伪代码
class CounterLimiter:
    limit = 100           // 每秒最大请求数
    counter = 0           // 当前计数
    windowStart = now()   // 窗口起始时间

    func allow():
        if now() - windowStart >= 1s:
            counter = 0
            windowStart = now()
        if counter < limit:
            counter++
            return true
        return false

2.2 滑动窗口算法(Sliding Window Algorithm)

滑动窗口算法将固定窗口细分为多个小格子(如 1 秒窗口分为 10 个 100ms 格子),每个格子独立计数。窗口随着时间推移“滑动”,始终统计最近 N 个格子的总请求数,从而平滑边界效应。

性能提示:滑动窗口的精度由格子数量决定。格子越多,边界越平滑,但内存和计算开销线性增长。生产环境中通常将 1 秒分为 10~20 个格子,在精度与性能间取得平衡。
对比维度 计数器算法 滑动窗口算法
窗口边界突发 严重(可达 2 倍阈值) 轻微(取决于格子数)
内存开销 O(1) O(N)(N 为格子数)
实现复杂度 低 中
适用场景 对突发不敏感、资源极受限 需要较精确限流、可接受少量内存
滑动窗口算法原理(1 秒窗口,10 个格子) 0ms 1000ms 100ms 当前滑动窗口(最近 1 秒) 12 8 15 3 20 7 11 9 14 6 当前窗口总请求数 = 105 窗口每 100ms 向前滑动一个格子 丢弃最旧格子,加入最新格子,重新求和

2.3 漏桶算法(Leaky Bucket Algorithm)

漏桶算法将请求视为水滴,以固定速率从桶底“漏出”处理;桶本身有容量上限,当桶满时新请求被丢弃。无论请求到达的速率如何波动,漏桶始终以恒定速率处理请求,实现完美的流量整形。

关键限制:漏桶算法无法应对突发流量——即使系统有能力处理瞬时高峰,漏桶也会强制将突发请求排队或丢弃。对于需要快速响应突发请求的低延迟 API,漏桶可能不是最佳选择。

2.4 令牌桶算法(Token Bucket Algorithm)

令牌桶算法以固定速率向桶中添加令牌,桶有容量上限。每个请求需要消耗一个令牌才能被处理。与漏桶不同,令牌桶允许一定程度的突发——当桶中积累了大量令牌时,可以一次性处理多个请求。

低延迟 API 首选:令牌桶算法在平滑限流与允许突发之间取得了最佳平衡,是生产环境中最常用的限流算法。它既能防止系统过载,又能利用空闲容量处理瞬时高峰。
// 令牌桶算法伪代码
class TokenBucketLimiter:
    capacity = 100        // 桶容量
    tokens = 100          // 当前令牌数
    rate = 50             // 每秒生成令牌数
    lastRefill = now()    // 上次补充时间

    func allow():
        now = now()
        elapsed = now - lastRefill
        tokens = min(capacity, tokens + elapsed * rate)
        lastRefill = now
        if tokens >= 1:
            tokens--
            return true
        return false
令牌桶算法 vs 漏桶算法 对比 令牌桶算法 桶容量 = 100 当前令牌数 = 6 ← 每秒生成 50 个令牌 → 每个请求消耗 1 个令牌 允许突发:桶满时可处理 100 个请求 漏桶算法 桶容量 = 100 当前水量 = 9 ← 固定速率漏出(50 请求/秒) → 请求以任意速率进入 不允许突发:超出部分排队或丢弃

2.5 四种算法综合对比

算法 突发处理 流量整形 内存开销 实现复杂度 低延迟 API 推荐度
计数器 差(边界突发) 无 O(1) 极低 ★☆☆☆☆
滑动窗口 中(取决于格子数) 弱 O(N) 中 ★★★☆☆
漏桶 不允许 强(完全平滑) O(1) 低 ★★☆☆☆
令牌桶 允许(桶容量内) 中(平滑但允许突发) O(1) 低 ★★★★★
选型建议:对于低延迟模型 API,推荐优先采用令牌桶算法作为核心限流策略。它既能通过令牌生成速率控制平均请求率,又能通过桶容量允许合理的突发请求,完美匹配模型推理场景中“平时低负载、偶尔批量请求”的流量特征。若业务要求严格的请求间隔(如避免同时打满 GPU 显存),可考虑在令牌桶外层叠加漏桶进行二次整形。