4. 限流算法基础:固定窗口、滑动窗口、令牌桶、漏桶算法的原理与适用场景

在自动化 Runbook 与自愈流程中,限流是防止级联故障、保护下游依赖的第一道防线。不同的限流算法在突发处理、平滑度、内存开销上差异显著,选型直接影响自愈策略的稳定性。本节从原理出发,对比四种经典算法的核心机制与适用场景。

4.1 固定窗口算法(Fixed Window)

固定窗口将时间划分为固定长度的时间片(如 1 秒),每个窗口内维护一个计数器,请求到达时计数器加 1;若计数器超过阈值则拒绝请求,窗口结束时计数器归零。

核心特征:实现极简,仅需一个计数器 + 窗口边界时间戳;内存占用 O(1)。
固定窗口限流示意图(窗口 1s,阈值 5) 窗口 1(0s ~ 1s) 请求数:5(已达阈值) 窗口 2(1s ~ 2s) 请求数:3(未达阈值) 窗口内超限拒绝
维度说明
优点实现简单,内存开销极低;适合对突发不敏感、请求均匀的场景
缺点窗口边界处可能出现两倍流量突发(临界突变);无法平滑流量
适用场景API 配额统计、简单访问频率控制、非关键路径的粗粒度限流
自愈陷阱:固定窗口在窗口切换瞬间允许突发两倍阈值流量,若下游服务抗性不足,Runbook 触发自愈后可能再次被击穿,形成震荡。建议仅在自愈降级阶段使用。

4.2 滑动窗口算法(Sliding Window)

滑动窗口将固定窗口细分为更小的子窗口(如 1s 窗口拆为 10 个 100ms 子窗口),维护一个环形队列或时间戳列表,每次请求时统计当前时间往前一个完整窗口内的请求总数,超过阈值则拒绝。

精度与代价:子窗口粒度越细,限流精度越高,但内存占用线性增长。通常 10~20 个子窗口即可达到较好的平滑效果。
滑动窗口限流示意图(1s 窗口,10 个子窗口) 0 1 2 3 4 5 6 7 8 9 当前滑动窗口(统计子窗口 0~9 的总请求数) 每个子窗口独立计数,窗口整体随当前时间右移 旧子窗口过期后自动丢弃,避免边界突发
维度说明
优点消除固定窗口的边界突发问题;精度可配置;实现相对简单
缺点需要维护多个子窗口计数器,内存开销高于固定窗口;仍为近似统计
适用场景网关限流、API 速率限制、需要较精确控制流量峰值的场景

4.3 令牌桶算法( Bucket)

令牌桶以固定速率向桶中添加令牌,桶容量上限为 burst。每个请求消耗一个令牌,桶空时拒绝请求。该算法允许一定程度的突发,但总体速率受限于令牌生成速率。

关键参数:令牌生成速率 r(个/秒)、桶容量 b(最大突发量)。瞬时流量可达 b,长期平均速率趋近 r。
令牌桶算法原理图 令牌桶(容量 b=5) T T T T T 令牌生成器 速率 r 个/秒 请求消耗令牌 桶空 → 拒绝
维度说明
优点允许突发流量,同时限制长期平均速率;实现简单,性能高
缺点突发量受桶容量限制;长时间空闲后桶满,无法累积更多突发能力
适用场景网络流量整形、微服务间调用限流、自愈流程中允许短时重试突发的场景
自愈最佳实践:令牌桶是 Runbook 中最常用的限流算法。自愈触发后的重试风暴可通过设置较小的桶容量 b 和适中的填充速率 r 来平滑,避免重试请求打垮正在恢复的服务。

4.4 漏桶算法(Leaky Bucket)

漏桶将请求视为水滴,以固定速率从桶底漏出(处理),桶满时新请求被丢弃。漏桶强制输出速率恒定,无论输入如何突发,输出始终平滑。

与令牌桶的本质区别:令牌桶允许突发输出(只要桶内有令牌),漏桶强制恒定输出(无论输入多快)。
漏桶算法原理图 漏桶(容量固定) 突发请求输入 恒定速率输出 漏口
维度说明
优点输出速率平滑,适合保护脆弱的下游;实现简单
缺点无法应对突发流量,即使下游有能力处理也会被限流;对突发业务不友好
适用场景数据库连接池限流、消息队列消费速率控制、对延迟敏感的下游保护

4.5 四种算法对比总结

算法突发能力输出平滑度内存开销实现复杂度自愈推荐度
固定窗口高(边界突发)低O(1)★☆☆☆☆低(易震荡)
滑动窗口中(可配置)中O(n)★★☆☆☆中
令牌桶高(受桶容量限制)中高O(1)★★☆☆☆高
漏桶无最高O(1)★★☆☆☆中高(保护下游)
选型建议:在自动化 Runbook 自愈流程中,令牌桶是最均衡的选择——允许自愈初期的重试突发,同时限制长期速率。若下游服务恢复能力极弱(如单线程处理),应切换为漏桶强制平滑。固定窗口和滑动窗口更适合统计配额,不建议在自愈关键路径上单独使用。