4. 限流算法基础:固定窗口、滑动窗口、令牌桶、漏桶算法的原理与适用场景
在自动化 Runbook 与自愈流程中,限流是防止级联故障、保护下游依赖的第一道防线。不同的限流算法在突发处理、平滑度、内存开销上差异显著,选型直接影响自愈策略的稳定性。本节从原理出发,对比四种经典算法的核心机制与适用场景。
4.1 固定窗口算法(Fixed Window)
固定窗口将时间划分为固定长度的时间片(如 1 秒),每个窗口内维护一个计数器,请求到达时计数器加 1;若计数器超过阈值则拒绝请求,窗口结束时计数器归零。
核心特征:实现极简,仅需一个计数器 + 窗口边界时间戳;内存占用 O(1)。
| 维度 | 说明 |
|---|---|
| 优点 | 实现简单,内存开销极低;适合对突发不敏感、请求均匀的场景 |
| 缺点 | 窗口边界处可能出现两倍流量突发(临界突变);无法平滑流量 |
| 适用场景 | API 配额统计、简单访问频率控制、非关键路径的粗粒度限流 |
自愈陷阱:固定窗口在窗口切换瞬间允许突发两倍阈值流量,若下游服务抗性不足,Runbook 触发自愈后可能再次被击穿,形成震荡。建议仅在自愈降级阶段使用。
4.2 滑动窗口算法(Sliding Window)
滑动窗口将固定窗口细分为更小的子窗口(如 1s 窗口拆为 10 个 100ms 子窗口),维护一个环形队列或时间戳列表,每次请求时统计当前时间往前一个完整窗口内的请求总数,超过阈值则拒绝。
精度与代价:子窗口粒度越细,限流精度越高,但内存占用线性增长。通常 10~20 个子窗口即可达到较好的平滑效果。
| 维度 | 说明 |
|---|---|
| 优点 | 消除固定窗口的边界突发问题;精度可配置;实现相对简单 |
| 缺点 | 需要维护多个子窗口计数器,内存开销高于固定窗口;仍为近似统计 |
| 适用场景 | 网关限流、API 速率限制、需要较精确控制流量峰值的场景 |
4.3 令牌桶算法( Bucket)
令牌桶以固定速率向桶中添加令牌,桶容量上限为 burst。每个请求消耗一个令牌,桶空时拒绝请求。该算法允许一定程度的突发,但总体速率受限于令牌生成速率。
关键参数:令牌生成速率
r(个/秒)、桶容量 b(最大突发量)。瞬时流量可达 b,长期平均速率趋近 r。
| 维度 | 说明 |
|---|---|
| 优点 | 允许突发流量,同时限制长期平均速率;实现简单,性能高 |
| 缺点 | 突发量受桶容量限制;长时间空闲后桶满,无法累积更多突发能力 |
| 适用场景 | 网络流量整形、微服务间调用限流、自愈流程中允许短时重试突发的场景 |
自愈最佳实践:令牌桶是 Runbook 中最常用的限流算法。自愈触发后的重试风暴可通过设置较小的桶容量
b 和适中的填充速率 r 来平滑,避免重试请求打垮正在恢复的服务。
4.4 漏桶算法(Leaky Bucket)
漏桶将请求视为水滴,以固定速率从桶底漏出(处理),桶满时新请求被丢弃。漏桶强制输出速率恒定,无论输入如何突发,输出始终平滑。
与令牌桶的本质区别:令牌桶允许突发输出(只要桶内有令牌),漏桶强制恒定输出(无论输入多快)。
| 维度 | 说明 |
|---|---|
| 优点 | 输出速率平滑,适合保护脆弱的下游;实现简单 |
| 缺点 | 无法应对突发流量,即使下游有能力处理也会被限流;对突发业务不友好 |
| 适用场景 | 数据库连接池限流、消息队列消费速率控制、对延迟敏感的下游保护 |
4.5 四种算法对比总结
| 算法 | 突发能力 | 输出平滑度 | 内存开销 | 实现复杂度 | 自愈推荐度 |
|---|---|---|---|---|---|
| 固定窗口 | 高(边界突发) | 低 | O(1) | ★☆☆☆☆ | 低(易震荡) |
| 滑动窗口 | 中(可配置) | 中 | O(n) | ★★☆☆☆ | 中 |
| 令牌桶 | 高(受桶容量限制) | 中高 | O(1) | ★★☆☆☆ | 高 |
| 漏桶 | 无 | 最高 | O(1) | ★★☆☆☆ | 中高(保护下游) |
选型建议:在自动化 Runbook 自愈流程中,令牌桶是最均衡的选择——允许自愈初期的重试突发,同时限制长期速率。若下游服务恢复能力极弱(如单线程处理),应切换为漏桶强制平滑。固定窗口和滑动窗口更适合统计配额,不建议在自愈关键路径上单独使用。