固定窗口算法是限流算法中最简单、最直接的一种,也常被称为计数器算法。
- 核心思想
固定窗口算法将时间轴划分为固定大小的、连续的时间窗口(例如,每1分钟一个窗口),并为每个窗口设置一个请求计数器。
规则:在一个时间窗口内,请求数不能超过预设的阈值。
处理:当一个请求到达时,系统会检查当前时间所属的窗口。
如果该窗口的计数器未超过阈值,则请求被允许,计数器+1。
如果该窗口的计数器已超过阈值,则请求被拒绝(被限流)。
- 工作原理
我们通过一个例子来理解。假设我们设置的限制规则是:每分钟最多处理 5 个请求。
时间线分析:
窗口 1 [00:00 – 01:00):
请求在 00:10, 00:20, 00:30, 00:40, 00:50 到达。计数器从 0 增加到 5。
此时,在 00:55 到达的第6个请求会被拒绝。
窗口切换 [01:00):
时间到达 01:00 整点,系统会立即重置计数器。当前窗口变为 [01:00 – 02:00),计数器归零。
窗口 2 [01:00 – 02:00):
请求在 01:05 到达,计数器变为 1。
请求在 01:10 到达,计数器变为 2。
… 以此类推。
- 实现方式(伪代码)
通常需要一个变量来记录当前窗口的请求数,以及一个变量记录当前窗口的结束时间戳。
python
class FixedWindowRateLimiter:
def init(self, max_requests, window_size_seconds):
self.max_requests = max_requests # 窗口内最大请求数,例如 5
self.window_size = window_size_seconds # 窗口大小,例如 60(秒)
self.current_count = 0 # 当前窗口的计数器
self.window_end = time.time() + self.window_size # 当前窗口的结束时间
def allow_request(self):
current_time = time.time()
# 检查是否已经进入下一个时间窗口
if current_time > self.window_end:
# 重置窗口:计数器归零,设置新的窗口结束时间
self.current_count = 0
self.window_end = current_time + self.window_size
# 检查当前窗口的请求数是否已超限
if self.current_count < self.max_requests:
self.current_count += 1
return True # 允许请求
else:
return False # 拒绝请求
- 优点
实现简单:逻辑非常直观,易于理解和编码。
内存效率高:只需要存储很少的几个状态(计数器和窗口结束时间)。
性能开销小:每次请求只有一些简单的比较和加法操作。
- 缺点与边界问题
这是固定窗口算法最主要的问题:在窗口边界附近可能会遭遇两倍的流量冲击。
问题场景:
继续上面的例子(限制:每分钟5个请求)。
假设在第一个窗口 [00:00 - 01:00) 的最后时刻(00:59)突然涌入了 5 个请求。由于窗口尚未重置,这 5 个请求会被正常处理。
下一秒,时间进入第二个窗口 [01:00 - 02:00) 的开始时刻(01:00),计数器重置。
此时,如果又突然涌入了 5 个请求,这 5 个请求也会被正常处理。
结果:
在短短 2 秒内(00:59 到 01:00),系统处理了 10 个请求。这远远超过了我们设定的“每分钟5个”的限制,可能会给系统带来意想不到的压力。
- 应用场景
尽管有边界缺陷,但由于其简单性,固定窗口算法仍然适用于一些要求不高的场景:
对限流精度要求不高的简单应用。
内部系统,即使有少量突发流量也不会造成严重后果。
作为更复杂限流算法的一个基础组件。
- 总结与对比
特性 固定窗口算法 滑动窗口算法(其改进版)
原理 固定时间片,计数器清零 将窗口细分为更小的格子,滑动更新
实现复杂度 简单 相对复杂
内存消耗 低 中等(需要存储子窗口信息)
精度 低,有边界突发问题 高,能平滑流量,更精确
突发流量处理 差(窗口边界双倍流量) 好(能够平滑过渡)
结论:
固定窗口算法是一个很好的限流入门算法,但它致命的边界问题使得它不适合在对稳定性要求高的生产环境中单独使用。在实际应用中,滑动窗口算法、漏桶算法或令牌桶算法通常是更好的选择。