关于 rate-limiting
面向开发者的限流实现参考,介绍 Express 和 Spring Boot 示例、Redis 滑动窗口及常见算法,帮助选择限流策略并配置响应头。
主要功能
- 01Express 限流示例
- 02Redis 滑动窗口实现
- 03限流算法比较
- 04Spring Boot 与 Bucket4j 示例
- 05响应头与生产检查清单
适用场景
- 01为 API 添加请求速率限制
- 02在多服务器部署中使用 Redis 限流
- 03比较不同限流算法
- 04配置 429 响应及重试提示
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill rate-limiting安装后在 Claude Code 中输入/rate-limiting确认已加载。
详细介绍
速率限制
Express (express-rate-limit)
import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
import { createClient } from 'redis';
const redis = createClient({ url: process.env.REDIS_URL });
// Global rate limit
const globalLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100,
standardHeaders: 'draft-7',
legacyHeaders: false,
store: new RedisStore({ sendCommand: (...args) => redis.sendCommand(args) }),
message: { error: 'Too many requests, try again later' },
});
app.use(globalLimiter);
// Stricter limit on auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
limit: 5,
keyGenerator: (req) => req.body.email || req.ip, // By email, not just IP
store: new RedisStore({ sendCommand: (...args) => redis.sendCommand(args) }),
});
app.use('/api/auth/login', authLimiter);
Redis 滑动窗口(自定义)
async function isRateLimited(key: string, limit: number, windowSec: number): Promise<boolean> {
const now = Date.now();
const windowStart = now - windowSec * 1000;
const multi = redis.multi();
multi.zRemRangeByScore(key, 0, windowStart); // Remove old entries
multi.zAdd(key, { score: now, value: `${now}` });
multi.zCard(key); // Count in window
multi.expire(key, windowSec);
const results = await multi.exec();
const count = results![2] as number;
return count > limit;
}
// Usage
if (await isRateLimited(`rl:${userId}`, 100, 60)) {
return res.status(429).json({ error: 'Rate limit exceeded' });
}
算法比较
| 算法 | 精确度 | 内存占用 | 最适用场景 |
|---|---|---|---|
| 固定窗口 | 低(窗口边界处会突发) | 极低 | 简单的 API |
| 滑动窗口日志 | 高 | 中等 | 精确限流 |
| 滑动窗口计数器 | 中等 | 低 | 平衡精确度与内存占用 |
| 令牌桶 | 中等 | 低 | 支持突发流量的 API |
Spring Boot (Bucket4j)
@Bean
public FilterRegistrationBean<RateLimitFilter> rateLimitFilter() {
var filter = new RateLimitFilter(
Bandwidth.classic(100, Refill.intervally(100, Duration.ofMinutes(1)))
);
var registration = new FilterRegistrationBean<>(filter);
registration.addUrlPatterns("/api/*");
return registration;
}
响应标头
// Standard rate limit headers (RFC draft-7)
res.set({
'RateLimit-Limit': '100',
'RateLimit-Remaining': `${remaining}`,
'RateLimit-Reset': `${resetTimestamp}`,
'Retry-After': `${retryAfterSeconds}`, // On 429 responses
});
反模式
| 反模式 | 修复方法 |
|---|---|
| 多服务器环境下使用内存限流 | 使用基于 Redis 的存储 |
| 仅按 IP 限流 | 同时按用户 ID 或 API 密钥限流 |
| 响应中没有速率限制标头 | 发送 RateLimit-* 标头,便于客户端了解限流情况 |
| 所有端点使用相同的限制 | 对身份验证、webhook 和高开销操作设置更严格的限制 |
| 429 响应中没有 Retry-After | 添加 Retry-After 标头 |
生产环境检查清单
- 在分布式部署中使用基于 Redis 的限流
- 按端点配置速率限制
- 在响应中添加标准速率限制标头
- 在 429 响应中添加 Retry-After 标头
- 按用户 ID 限流(不只是 IP)
- 监控:触发限流的频率
评论
支持 Markdown