限流策略指南
算法对比
令牌桶
以固定速率添加令牌。请求消耗令牌。允许突发到桶大小。
✓ 支持突发,平均速率平滑
漏桶
请求排队。以恒定速率处理,无论输入如何。输出非常平滑。
✓ 输出平滑,无突发
固定窗口
每时间窗口允许N个请求(如100/分钟)。窗口边界重置。简单但边界可能突发。
✓ 简单,易理解
滑动窗口
在滚动时间窗口内计数请求。比固定窗口更精确。内存使用更多。
✓ 精确,无边界突发
标准限流响应头
# Response headers (IETF draft standard) RateLimit-Limit: 100 # max requests per window RateLimit-Remaining: 45 # requests left RateLimit-Reset: 1704067200 # Unix timestamp when window resets # When limit exceeded: 429 Too Many Requests HTTP/1.1 429 Too Many Requests Retry-After: 30 # seconds to wait
Redis令牌桶(Go示例)
// Using go-redis with token bucket pattern
func rateLimitCheck(ctx context.Context, rdb *redis.Client, key string, limit int, window time.Duration) bool {
pipe := rdb.Pipeline()
now := time.Now().Unix()
windowStart := now - int64(window.Seconds())
// Remove expired entries
pipe.ZRemRangeByScore(ctx, key, "-inf", strconv.FormatInt(windowStart, 10))
// Count current window
pipe.ZCard(ctx, key)
// Add new request
pipe.ZAdd(ctx, key, &redis.Z{Score: float64(now), Member: now})
pipe.Expire(ctx, key, window)
results, _ := pipe.Exec(ctx)
count := results[1].(*redis.IntCmd).Val()
return count < int64(limit)
}
不同API类型建议限制
| API类型 | 典型限制 | 说明 |
|---|---|---|
| 公开读取API | 1000/hour | 未认证用户 |
| 认证API | 5000/hour | 每API key |
| 写入/修改API | 100/minute | 每用户 |
| 登录端点 | 10/minute | 防暴力破解 |
| 邮件/短信发送 | 5/minute | 每用户每操作 |