接口限流策略
为什么需要限流
限流是保护后端服务稳定性的重要手段。当流量超出系统处理能力时,限流可以:
- 防止服务雪崩:避免单个服务过载影响整个系统
- 公平分配资源:确保所有用户都能获得服务
- 防范恶意攻击:抵御 DDoS 和暴力破解
- 成本控制:控制 API 调用量,避免资源过度消耗
常见限流算法
计数器算法
最简单的实现,在固定时间窗口内计数:
type CounterLimiter struct {
limit int
window time.Duration
requests map[string]*counter
}
func (l *CounterLimiter) Allow(key string) bool {
now := time.Now()
c, ok := l.requests[key]
if !ok || now.Sub(c.windowStart) > l.window {
l.requests[key] = &counter{windowStart: now, count: 1}
return true
}
if c.count < l.limit {
c.count++
return true
}
return false
}
缺点:存在临界突变问题——窗口边界处的流量突增可能超过限制的两倍。
滑动窗口算法
将时间窗口细分为多个小格子,更精确地控制流量:
type SlidingWindowLimiter struct {
limit int
window time.Duration
slots int
requests map[string]*sync.Map
}
滑动窗口通过记录每个时间戳的请求,解决了计数器算法的边界突变问题。
令牌桶算法
以固定速率向桶中添加令牌,请求需获取令牌才能通过:
type TokenBucket struct {
rate float64 // 令牌添加速率(个/秒)
capacity int64 // 桶容量
tokens int64 // 当前令牌数
lastTime time.Time // 上次更新令牌的时间
mu sync.Mutex
}
func (tb *TokenBucket) Allow() bool {
tb.mu.Lock()
defer tb.mu.Unlock()
// 补充令牌
now := time.Now()
elapsed := now.Sub(tb.lastTime).Seconds()
tb.tokens = min(tb.capacity, tb.tokens + int64(elapsed * tb.rate))
tb.lastTime = now
if tb.tokens > 0 {
tb.tokens--
return true
}
return false
}
优点:允许突发流量(桶中积累的令牌),同时限制平均速率。
漏桶算法
请求以固定速率被处理,超出桶容量的请求被丢弃:
请求进入
↓
┌──────────────┐
│ 漏桶(队列) │ 容量 = N
└──────────────┘
↓(恒定速率)
处理请求
优点:输出流量完全平滑,适合保护下游系统。 缺点:无法利用空闲资源处理突发流量。
分布式限流
在分布式系统中,限流计数器需要共享存储:
Redis 实现
func AllowRequest(userID string, limit int, window time.Duration) bool {
key := fmt.Sprintf("rate_limit:%s:%d", userID, window.Seconds())
current, _ := redis.Incr(key)
if current == 1 {
redis.Expire(key, window)
}
return current <= limit
}
使用 Redis 的 INCR + EXPIRE 实现滑动窗口,或用 ZSET 实现精确滑动窗口:
func SlidingWindowAllow(userID string, limit int, window time.Duration) bool {
key := "rate_limit:" + userID
now := time.Now().UnixMilli()
windowStart := now - window.Milliseconds()
// 移除窗口外的记录
redis.ZRemRangeByScore(key, "0", strconv.FormatInt(windowStart, 10))
// 统计窗口内的请求数
count, _ := redis.ZCard(key)
if count < limit {
redis.ZAdd(key, &redis.Z{Score: float64(now), Member: now})
redis.Expire(key, window)
return true
}
return false
}
限流策略配置
分级限流
rate_limits:
# 用户级别
user:
free: 10/minute, 100/hour
pro: 100/minute, 1000/hour
enterprise: 1000/minute, unlimited/hour
# API 级别
api:
/api/login: 5/minute # 登录接口严格限流
/api/search: 30/minute # 搜索接口
/api/data: 100/minute # 数据接口
# 全局级别
global:
max_connections: 10000
max_requests: 100000/minute
限流响应
当请求被限流时,返回 429 Too Many Requests:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995200
{
"error": "rate_limit_exceeded",
"message": "请求过于频繁,请稍后重试",
"retry_after": 30
}