3. 常见限流算法(上):固定窗口计数器算法

固定窗口计数器算法(Fixed Window Counter)是所有限流算法中最直观、最容易实现的一种。它将时间划分为固定长度的窗口(例如 1 秒、1 分钟),在每个窗口内维护一个计数器,当请求到达时计数器加 1;如果计数器超过预设阈值,则拒绝后续请求;窗口结束时计数器重置。

3.1 算法原理

固定窗口算法的核心思想可以概括为三个步骤:

  1. 划分时间窗口:将时间轴均匀切分为固定长度的窗口(如 1 秒)。
  2. 计数累加:每个请求到达时,当前窗口的计数器加 1。
  3. 阈值判断:若计数器 ≤ 阈值,请求通过;否则请求被拒绝或排队。

窗口切换时,计数器立即归零,开始新一轮计数。

核心公式: 当前窗口请求数 ≤ 阈值 → 允许;否则 → 拒绝。
固定窗口计数器算法流程图 请求到达 当前窗口计数器 +1 计数器 ≤ 阈值? 是 请求通过 否 请求被拒绝

3.2 代码实现(Go 语言示例)

以下是一个基于 Go 语言的固定窗口计数器限流器实现,使用 sync.Mutex 保证并发安全:

package limiter

import (
    "sync"
    "time"
)

// FixedWindowLimiter 固定窗口限流器
type FixedWindowLimiter struct {
    mu       sync.Mutex
    limit    int           // 窗口内最大请求数
    window   time.Duration // 窗口大小
    count    int           // 当前窗口计数
    windowStart time.Time  // 当前窗口起始时间
}

// NewFixedWindowLimiter 创建固定窗口限流器
func NewFixedWindowLimiter(limit int, window time.Duration) *FixedWindowLimiter {
    return &FixedWindowLimiter{
        limit:       limit,
        window:      window,
        windowStart: time.Now(),
    }
}

// Allow 判断请求是否允许通过
func (l *FixedWindowLimiter) Allow() bool {
    l.mu.Lock()
    defer l.mu.Unlock()

    now := time.Now()
    // 如果当前时间已超出窗口范围,重置计数器
    if now.Sub(l.windowStart) >= l.window {
        l.count = 0
        l.windowStart = now
    }

    // 判断是否超过阈值
    if l.count < l.limit {
        l.count++
        return true
    }
    return false
}

3.3 优缺点分析

维度 说明
优点
  • 实现简单,仅需一个计数器 + 时间戳
  • 内存占用极低(O(1) 空间复杂度)
  • 性能高,仅一次整数比较和加法操作
  • 适合对突发流量不敏感的场景
缺点
  • 临界突变问题:窗口切换瞬间可能出现两倍阈值的突发流量
  • 无法平滑限制流量,窗口边界处流量分布不均匀
  • 不适合需要精确控制请求间隔的场景
⚠️ 临界突变问题详解: 假设限流阈值为 100 次/秒。如果在第 1 秒的最后 10ms 内通过了 100 个请求,紧接着第 2 秒的前 10ms 内又通过了 100 个请求,那么这 20ms 内实际通过了 200 个请求,是阈值的 2 倍。这是固定窗口算法最致命的缺陷。
固定窗口临界突变问题示意图(阈值 = 100 请求/秒) 0s 1s 2s 窗口 1 窗口 2 20ms 内 200 请求 窗口 1 末尾 10ms:100 请求 窗口 2 开头 10ms:100 请求 实际速率 = 200 请求 / 20ms = 10000 请求/秒(远超阈值)

3.4 适用场景

  • 对突发流量容忍度较高的 API:如非关键性的日志上报、统计埋点。
  • 粗粒度限流:分钟级或小时级的配额控制,临界突变影响较小。
  • 资源受限环境:嵌入式设备、边缘节点,需要极简实现。
  • 配合其他算法使用:作为前置快速过滤,再配合滑动窗口或令牌桶做精细控制。
💡 实践建议: 在生产环境中,如果使用固定窗口算法,建议将窗口粒度缩小(如 100ms 窗口)或配合随机丢弃策略来缓解临界突变问题。对于模型服务 API,通常推荐使用滑动窗口或令牌桶算法以获得更平滑的限流效果。