3、限流基础算法(一):固定窗口计数器算法
固定窗口计数器算法是限流领域最直观、最容易实现的算法之一。它将时间划分为固定的窗口(例如 1 秒、1 分钟),在每个窗口内维护一个计数器,当请求到达时计数器加 1;如果计数器超过预设阈值,则拒绝后续请求,直到下一个时间窗口开始,计数器重置。
3.1 算法原理
核心思想:以固定的时间粒度切分流量,在每个粒度内独立计数。算法不关心窗口内的请求分布是否均匀,只关心窗口总请求数是否越界。
3.2 实现步骤
固定窗口计数器的实现非常简洁,核心只需要维护一个计数器和一个时间戳。以下是标准实现步骤:
- 初始化:设置窗口大小
windowSize(毫秒)和阈值limit;初始化计数器counter = 0,记录当前窗口起始时间windowStart = now()。 - 请求到达:获取当前时间
currentTime。 - 窗口判断:如果
currentTime - windowStart > windowSize,说明已进入下一个时间窗口,重置counter = 0,更新windowStart = currentTime。 - 阈值检查:如果
counter < limit,允许请求通过,counter++;否则拒绝请求。
伪代码实现:
class FixedWindowRateLimiter:
def __init__(self, window_size_ms, limit):
self.window_size = window_size_ms
self.limit = limit
self.counter = 0
self.window_start = current_time_ms()
def allow_request(self):
now = current_time_ms()
if now - self.window_start > self.window_size:
self.counter = 0
self.window_start = now
if self.counter < self.limit:
self.counter += 1
return True
return False
3.3 优缺点分析
| 维度 | 说明 |
|---|---|
| 优点 |
|
| 缺点 |
|
3.4 适用场景
固定窗口计数器算法虽然存在临界突刺问题,但在以下场景中仍然非常实用:
| 场景 | 适用原因 |
|---|---|
| 粗粒度限流 | 对时间窗口内的请求总数有硬性上限要求,不关心窗口内的瞬时波动。例如:每分钟最多允许 1000 次 API 调用。 |
| 资源预算控制 | 用于控制每日/每小时的资源消耗总量,如云服务配额、短信发送量、存储写入次数。 |
| 原型验证 | 在项目早期快速实现限流功能,后续再替换为更平滑的算法(如滑动窗口、令牌桶)。 |
| 低并发系统 | 系统 QPS 较低(如 < 100),临界突刺的影响在可接受范围内。 |
| 边缘设备/嵌入式 | 内存和计算资源受限,无法承载复杂算法。 |
实践建议:在生产环境中,如果选择固定窗口算法,建议将窗口大小设置为业务可接受的最小粒度(例如 100ms 而非 1s),以降低突刺窗口的绝对时长。同时配合后端服务的熔断机制,形成双层保护。
3.5 与后续算法的对比
固定窗口计数器是限流算法家族的基石。理解它的优缺点,有助于后续学习更高级的算法:
| 对比维度 | 固定窗口 | 滑动窗口日志 | 令牌桶 | 漏桶 |
|---|---|---|---|---|
| 实现复杂度 | ★☆☆☆☆ | ★★★☆☆ | ★★☆☆☆ | ★★☆☆☆ |
| 内存占用 | 极低 | 较高 | 低 | 低 |
| 临界突刺 | 有 | 无 | 无 | 无 |
| 流量平滑度 | 差 | 较好 | 好 | 最好 |
| 突发处理 | 不支持 | 不支持 | 支持 | 不支持 |