2. 限流算法基础(一):计数器与滑动窗口

在金融API网关中,限流是保障系统稳定性的第一道防线。本章深入剖析两种最基础的限流算法——计数器算法与滑动窗口算法,从原理、实现到金融场景的适用性,为你打下坚实的限流理论基础。

2.1 计数器算法(Fixed Window Counter)

2.1.1 原理与实现

计数器算法将时间划分为固定的窗口(如1秒、1分钟),每个窗口内维护一个计数器。请求到达时,若计数器未超过阈值则放行并计数+1;若超过阈值则拒绝。窗口结束时,计数器重置为0。

核心逻辑伪代码:
class FixedWindowCounter:
    def __init__(self, limit, window_ms):
        self.limit = limit          # 窗口内最大请求数
        self.window_ms = window_ms  # 窗口大小(毫秒)
        self.counter = 0
        self.window_start = now()

    def allow(self):
        if now() - self.window_start > self.window_ms:
            # 新窗口,重置
            self.window_start = now()
            self.counter = 0
        if self.counter < self.limit:
            self.counter += 1
            return True
        return False

2.1.2 优缺点分析

维度 说明
优点 实现简单,内存占用极低(仅需2个变量);时间复杂度O(1);适合对精度要求不高的场景
缺点 存在窗口边界突刺问题:在窗口末尾和下一个窗口开头,可能短时间内通过2倍阈值的请求量
适用场景 非关键路径的粗粒度限流、后台批处理任务、对突发容忍度较高的系统
金融场景避坑: 在支付、下单等核心链路中,计数器算法的边界突刺可能导致瞬间流量翻倍,引发数据库连接池打满或下游服务雪崩。务必谨慎使用。

2.1.3 边界突刺问题图解

下图展示了计数器算法在窗口切换时的典型突刺现象:

计数器算法边界突刺示意图 窗口1(0~1000ms) 窗口2(1000~2000ms) 0ms 1000ms 2000ms 突刺区域:窗口1末尾(950~1000ms)与窗口2开头(1000~1050ms) 若阈值=100,则100ms内可能通过200个请求,远超预期 窗口末尾 窗口开头

2.2 滑动窗口算法(Sliding Window)

2.2.1 原理与实现

滑动窗口算法将时间窗口细分为多个小格子(如1秒窗口分为10个100ms的格子),每个格子独立计数。窗口随着时间推移“滑动”,始终统计最近N个格子的总请求数。当总请求数超过阈值时拒绝新请求。

核心逻辑伪代码:
class SlidingWindowCounter:
    def __init__(self, limit, window_ms, slot_ms):
        self.limit = limit
        self.slot_count = window_ms // slot_ms
        self.slot_ms = slot_ms
        self.slots = [0] * self.slot_count
        self.current_index = 0
        self.last_slot_time = now()

    def allow(self):
        self._advance_window()
        total = sum(self.slots)
        if total < self.limit:
            self.slots[self.current_index] += 1
            return True
        return False

    def _advance_window(self):
        elapsed = now() - self.last_slot_time
        slots_to_advance = elapsed // self.slot_ms
        if slots_to_advance > 0:
            # 滑动窗口,重置过期的格子
            for i in range(min(slots_to_advance, self.slot_count)):
                self.current_index = (self.current_index + 1) % self.slot_count
                self.slots[self.current_index] = 0
            self.last_slot_time += slots_to_advance * self.slot_ms

2.2.2 优缺点分析

维度 说明
优点 有效消除边界突刺,限流曲线平滑;精度可通过调整格子数量控制;适合对流量稳定性要求高的场景
缺点 内存占用随格子数线性增长(通常10~100个格子即可);实现复杂度略高于计数器;格子数过多时CPU开销增加
适用场景 金融核心交易链路、API网关、对突发流量敏感的实时系统

2.2.3 滑动窗口工作流程

下图展示了滑动窗口如何通过细粒度格子实现平滑限流:

滑动窗口算法工作流程 0~100ms 100~200ms 200~300ms 300~400ms 400~500ms 500~600ms 600~700ms 700~800ms 800~900ms 900~1000ms 当前滑动窗口(最近1000ms) 当前时间 滑动窗口核心机制 • 将1秒窗口细分为10个100ms的格子,每个格子独立计数 • 窗口每100ms滑动一次:丢弃最旧格子,新增一个空格子 • 总请求数 = 当前10个格子计数之和,超过阈值则拒绝 效果:消除边界突刺,限流曲线平滑 任何100ms内的请求数 ≤ 阈值,不会出现翻倍现象

2.3 两种算法对比总结

对比维度 计数器算法 滑动窗口算法
实现复杂度 极低(2个变量) 中等(数组+指针维护)
内存占用 O(1) O(N),N为格子数(通常10~100)
边界突刺 严重(可达2倍阈值) 无(平滑过渡)
限流精度 粗粒度(窗口级别) 细粒度(格子级别)
金融场景推荐 ❌ 不推荐核心链路 ✅ 推荐用于交易、支付等