3. 限流算法基础:固定窗口算法原理与Go实现
固定窗口算法(Fixed Window Algorithm)是限流领域最直观、最容易实现的算法之一。它将时间划分为固定长度的窗口(如1秒、1分钟),在每个窗口内维护一个计数器,当请求到达时计数器加1;若计数器超过预设阈值,则拒绝后续请求,直到下一个窗口开始计数器重置。
3.1 算法核心原理
固定窗口算法的运行机制可以概括为以下三个步骤:
- 时间窗口划分:将时间轴按固定长度(如1秒)切分为连续的窗口,每个窗口拥有独立的计数器。
- 请求计数:每个请求到达时,判断其所属的当前窗口,并将该窗口的计数器加1。
- 阈值判断与拒绝:若当前窗口计数器超过预设的阈值(如100 QPS),则拒绝该请求;否则正常放行。
关键参数:窗口大小(windowSize)和阈值(limit)。窗口大小决定了计数重置的频率,阈值决定了单位时间内允许的最大请求量。
3.2 固定窗口算法的Go实现
以下是一个线程安全的固定窗口限流器实现,使用 sync.Mutex 保证并发安全:
package fixedwindow
import (
"sync"
"time"
)
// FixedWindowLimiter 固定窗口限流器
type FixedWindowLimiter struct {
mu sync.Mutex
limit int // 窗口内最大请求数
windowSize time.Duration // 窗口大小
counter int // 当前窗口计数
lastTime time.Time // 上次窗口开始时间
}
// NewFixedWindowLimiter 创建固定窗口限流器
func NewFixedWindowLimiter(limit int, windowSize time.Duration) *FixedWindowLimiter {
return &FixedWindowLimiter{
limit: limit,
windowSize: windowSize,
lastTime: time.Now(),
}
}
// Allow 判断当前请求是否允许通过
func (l *FixedWindowLimiter) Allow() bool {
l.mu.Lock()
defer l.mu.Unlock()
now := time.Now()
// 如果当前时间已超过窗口结束时间,重置计数器
if now.Sub(l.lastTime) >= l.windowSize {
l.counter = 0
l.lastTime = now
}
// 判断是否超过阈值
if l.counter >= l.limit {
return false
}
l.counter++
return true
}
使用示例:
limiter := NewFixedWindowLimiter(100, time.Second) 创建一个每秒最多允许100个请求的限流器。
3.3 固定窗口算法的临界突变问题
固定窗口算法虽然简单,但存在一个严重的缺陷——临界突变问题(Critical Burst Problem)。该问题发生在窗口切换的边界处,导致实际流量可能达到阈值的两倍。
3.3.1 问题现象
假设限流阈值为100 QPS,窗口大小为1秒。考虑以下场景:
- 窗口A(0s~1s)的最后100ms内,突然涌入100个请求,此时窗口A的计数器达到阈值,后续请求被拒绝。
- 紧接着窗口B(1s~2s)开始,计数器重置为0。在窗口B的前100ms内,又涌入100个请求。
- 实际上,在200ms的时间跨度内(0.9s~1.1s),系统承受了200个请求,远超100 QPS的阈值。
下图直观展示了临界突变问题的产生过程:
3.3.2 问题根源分析
临界突变问题的根本原因在于:固定窗口算法只关心窗口内的总计数,而不关心请求在窗口内的分布情况。窗口边界处的计数器重置,使得相邻窗口的请求可以在极短时间内集中到达,绕过限流保护。
| 对比维度 | 正常场景 | 临界突变场景 |
|---|---|---|
| 请求分布 | 均匀分布在1秒内 | 集中在窗口边界两侧 |
| 实际QPS | ≤ 100 | 可达200 |
| 限流效果 | 有效保护 | 限流失效 |
| 对系统影响 | 稳定 | 可能引发雪崩 |
实际危害:在微服务网关、API限流等场景中,临界突变问题可能导致后端服务在短时间内承受双倍流量冲击,引发连接池耗尽、响应超时甚至服务雪崩。该问题在流量突增的秒杀、抢购场景下尤为致命。
3.4 固定窗口算法的适用场景
尽管存在临界突变问题,固定窗口算法在以下场景中仍然适用:
- 对突发流量不敏感:如日志收集、批量任务调度等允许短时波动的场景。
- 粗粒度限流:分钟级或小时级的限流,窗口边界处的突发影响相对较小。
- 作为兜底策略:与其他限流算法(如滑动窗口、令牌桶)配合使用,提供基础保护。
3.5 本章小结
固定窗口算法是限流领域的入门级算法,实现简单、性能优异,但其临界突变问题限制了在高精度限流场景中的应用。理解该问题的本质——窗口边界处的计数器重置导致流量聚集——是后续学习滑动窗口算法和令牌桶算法的重要基础。在实际项目中,建议根据业务对突发流量的容忍度,选择是否使用固定窗口算法或升级为更平滑的限流方案。