2、限流基础理论:四大核心算法原理

限流(Rate Limiting)是微服务网关与业务层的第一道防线。在 Gin 项目中,选择正确的限流算法直接决定了系统的吞吐、延迟与公平性。本节深入剖析四种经典限流算法的核心原理、数学模型与适用场景,为后续在 Gin 中间件中落地限流能力奠定理论基础。

2.1 计数器算法(Fixed Window Counter)

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

核心缺陷:临界突变问题。若窗口边界处出现突发流量(如第 1 秒最后 10ms 和第 2 秒前 10ms 各通过 100 个请求),实际 20ms 内通过了 200 个请求,远超阈值。
维度 说明
窗口粒度 固定时间长度(如 1s / 1min)
计数器 原子整数(atomic int)
重置策略 窗口到期立即归零
主要问题 窗口边界流量突刺
适用场景 低精度、简单限流需求
计数器算法原理示意图 0ms 1000ms 2000ms 窗口1(计数=100) 窗口2(计数=100) 临界点 窗口1末尾 10ms 通过 100 请求 窗口2开头 10ms 通过 100 请求

2.2 滑动窗口算法(Sliding Window Log / Sliding Window Counter)

滑动窗口算法将固定窗口细分为多个小格(如 1 秒分为 10 个 100ms 小格),每个小格独立计数。窗口随着时间推移向前滑动,丢弃过期小格,累加当前窗口内所有小格的计数。相比计数器算法,滑动窗口大幅缓解了临界突变问题。

工程实践:Gin 项目中常用 Redis Sorted Set 实现滑动窗口,以时间戳为 score,每次请求添加成员并移除窗口外的过期成员,通过 ZCARD 获取当前窗口请求数。
维度 计数器算法 滑动窗口算法
窗口粒度 1 个粗粒度窗口 N 个细粒度子窗口
临界突刺 严重 大幅缓解(子窗口越多越平滑)
内存开销 低(1 个计数器) 较高(N 个计数器 + 时间戳列表)
实现复杂度 低 中
滑动窗口算法原理示意图 0ms 1000ms 2000ms 当前滑动窗口(10个子窗口) 每个子窗口独立计数,窗口整体向右滑动 最左侧子窗口过期移除,最右侧新增子窗口

2.3 漏桶算法(Leaky Bucket)

漏桶算法将请求视为水滴,以恒定速率从桶底漏出。桶有固定容量,当桶满时新请求被丢弃。漏桶强制输出速率恒定,无论输入流量如何突发,输出始终平滑。

注意:漏桶算法对突发流量不友好。即使桶未满,也无法加速处理积压请求;适合需要严格平滑输出的场景(如数据库写入、下游慢服务保护)。
  • 桶容量(capacity):最大积压请求数,对应系统容忍的瞬时突发量。
  • 漏出速率(leak rate):每秒处理请求数,对应下游服务的最大吞吐。
  • 当前水位(water level):桶内积压请求数,随时间递减。
  • 丢弃策略:桶满时新请求直接拒绝(HTTP 429 Too Many Requests)。
漏桶算法原理示意图 水位 请求流入(突发) 恒定速率流出 桶满丢弃

2.4 令牌桶算法(Token Bucket)

令牌桶算法是目前业界最广泛使用的限流算法(如 Guava RateLimiter、gRPC 流控)。它以固定速率向桶中放入令牌,每个请求消耗一个令牌;桶有最大容量,令牌超过容量则丢弃。当桶中令牌耗尽时,请求被限流。

优势:令牌桶允许一定程度的突发流量(桶内积攒的令牌可被一次性消耗),同时保证长期平均速率不超过阈值。相比漏桶,令牌桶更适合 Web API 场景。
特性 令牌桶 漏桶
输出速率 允许突发(桶内有令牌时) 严格恒定
桶容量含义 最大突发请求数 最大积压请求数
丢弃时机 令牌不足时 桶满时
适用场景 API 网关、微服务入口 下游慢服务保护
令牌桶算法原理示意图 令牌池(最大容量 N) 固定速率放入令牌 请求消耗 1 个令牌

2.5 四种算法对比总结

算法 突发容忍 输出平滑度 内存开销 实现复杂度 Gin 推荐度
计数器 差(临界突刺) 差 极低 极低 ⭐(不推荐)
滑动窗口 中(子窗口越多越好) 中 中 中 ⭐⭐⭐
漏桶 无 极高 低 低 ⭐⭐(下游保护)
令牌桶 好 高 低 低 ⭐⭐⭐⭐⭐
Gin 项目选型建议:入口网关层优先选择令牌桶算法(支持突发、实现简单);下游脆弱服务保护优先选择漏桶算法(强制平滑);分布式场景下可结合 Redis 实现滑动窗口算法。计数器算法仅用于非关键路径的简易限流。