Node.js 中的速率限制:保护 API 免受过载和攻击
速率限制(Rate limiting)是任何 Node.js Web 应用程序的关键机制。它允许您控制客户端在特定时间间隔内向服务器发送的请求数量。如果没有速率限制,您的 API 将容易受到 DDoS 攻击、意外过载和资源过度消耗的影响。
为什么需要速率限制?
- 防止暴力破解:阻止多次登录尝试。
- 防止 DDoS:限制拒绝服务攻击的影响。
- 公平分配资源:确保所有用户享有平等访问权。
- 节省成本:降低服务器和数据库的负载。
- 遵守 SLA:履行服务级别协议。
主要的速率限制算法
有几种实现速率限制的方法。让我们看看最流行的几种:
1. 滑动窗口
最常见的算法。请求被分组到时间窗口(例如 1 分钟)中。如果请求数量超过限制,后续请求将被拒绝,直到新窗口开始。
2. 令牌桶
一个虚拟的“桶”包含令牌。每个请求消耗一个令牌。令牌以固定速率补充。如果桶为空,请求将被拒绝。
3. 漏桶
请求进入队列并以恒定速率处理。如果队列溢出,请求将被丢弃。
Node.js 的速率限制库
1. express-rate-limit
最流行的 Express.js 库。易于使用且可配置。
const rateLimit = require('express-rate-limit');
// 创建用于限制请求的中间件const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 分钟 max: 100, // 每个窗口最多 100 个请求 message: '请求过多,请稍后再试。', standardHeaders: true, // 返回 RateLimit-* 标头 legacyHeaders: false, // 禁用 X-RateLimit-* 标头});
// 应用于所有路由app.use(limiter);
// 或仅应用于特定路由app.use('/api/login', limiter);2. bottleneck
一个通用的库,适用于任何异步操作。支持令牌桶和漏桶算法。
const Bottleneck = require('bottleneck');
// 创建限制器:每秒 5 个请求const limiter = new Bottleneck({ minTime: 200, // 请求之间间隔 200 毫秒 maxConcurrent: 5, // 最多 5 个并发请求 reservoir: 100, // 初始令牌数 reservoirRefreshAmount: 100, reservoirRefreshInterval: 60 * 1000, // 每分钟补充一次});
// 将函数包装在限制器中async function fetchData(url) { const response = await fetch(url); return response.json();}
const limitedFetch = limiter.wrap(fetchData);
// 使用const data = await limitedFetch('https://api.example.com/data');3. rate-limiter-flexible
一个灵活的库,支持 Redis、MongoDB、Memcached 和其他存储。非常适合分布式系统。
const { RateLimiterRedis } = require('rate-limiter-flexible');const Redis = require('ioredis');
const redisClient = new Redis();
const rateLimiter = new RateLimiterRedis({ storeClient: redisClient, keyPrefix: 'middleware', points: 10, // 10 个请求 duration: 1, // 在 1 秒内});
app.use(async (req, res, next) => { try { await rateLimiter.consume(req.ip); next(); } catch (rejRes) { res.status(429).send('请求过多'); }});