简介
在本教程中,你将学习如何使用 Node.js 和 TypeScript 语言构建一个速率限制器。
速率限制对于防止滥用、保护你的 API 以及管理资源至关重要。我们将使用 Node.js 构建一个生产级的速率限制器,并将其部署在现代应用程序平台上。该速率限制器将使用 Redis 来进行高效的状态管理,并集成到一个示例 API 应用程序中。
速率限制的关键概念
- 速率限制: 限制用户或客户端在特定时间窗口内可以发出的请求数量。
- 令牌桶算法: 一种流行的速率限制实现算法,它定期授予令牌,只有在有令牌可用时才允许请求。
- Redis: 一种高性能的内存数据存储,用于管理分布式系统(如 API)的状态。
本教程的目标是手把手教你如何在 Linux 服务器上安装并配置一个使用 Node.js 和 TypeScript 构建的速率限制器。
安装和配置步骤
第一步:创建一个 Node.js 应用程序
初始化项目
mkdir rate-limiter-app
cd rate-limiter-app
npm init -y
安装依赖
npm install express redis dotenv
安装开发工具
npm install --save-dev typescript @types/node ts-node @types/express
第二步:设置 Redis 用于速率限制
创建一个 redisClient.ts
文件来处理 Redis 交互:
nano redisClient.ts
添加以下代码:
import { createClient, RedisClientType } from 'redis';
import dotenv from 'dotenv';
// Load environment variables from .env file
dotenv.config();
class RedisClient {
private static instance: RedisClientType | null = null;
/**
* Initialize Redis client or return an existing instance.
*/
public static getInstance(): RedisClientType {
if (!RedisClient.instance) {
const redisUrl = process.env.REDIS_URL || 'redis://127.0.0.1:6379';
try {
// Create Redis client
RedisClient.instance = createClient({
url: redisUrl,
});
// Add error handling
RedisClient.instance.on('error', (err) => {
console.error(`Redis Client Error: ${err.message}`);
});
// Connect to Redis
RedisClient.instance.connect().catch((err) => {
console.error(`Failed to connect to Redis: ${err.message}`);
});
console.info(`Connected to Redis at ${redisUrl}`);
} catch (err) {
console.error('Error initializing Redis client:', err);
throw err;
}
}
return RedisClient.instance;
}
/**
* Disconnect Redis client (useful during app shutdown).
*/
public static async disconnect(): Promise<void> {
if (RedisClient.instance) {
await RedisClient.instance.quit();
RedisClient.instance = null;
console.info('Disconnected from Redis.');
}
}
}
export default RedisClient;
此设置连接到 Redis 实例,并确保客户端在整个应用程序中可用。
第三步:实现速率限制器中间件
创建一个 rateLimiter.ts
文件:
nano rateLimiter.ts
添加以下代码:
import { Request, Response, NextFunction } from 'express';
import { createClient, RedisClientType } from 'redis';
import dotenv from 'dotenv';
// Load environment variables
dotenv.config();
// Redis client setup
const redisClient: RedisClientType = createClient({
url: process.env.REDIS_URL || 'redis://127.0.0.1:6379',
});
// Connect Redis client
redisClient.connect().catch((err) => {
console.error('Error connecting to Redis:', err);
});
interface RateLimiterOptions {
windowMs: number; // Time window in milliseconds
maxRequests: number; // Maximum allowed requests in the time window
}
export const rateLimiter = (options: RateLimiterOptions) => {
const { windowMs, maxRequests } = options;
return async (req: Request, res: Response, next: NextFunction) => {
try {
const key = `rate-limit:${req.ip}`; // Unique key per user/IP
const currentCount = await redisClient.incr(key);
if (currentCount === 1) {
// Set expiry time for the key
await redisClient.expire(key, Math.ceil(windowMs / 1000));
}
if (currentCount > maxRequests) {
const ttl = await redisClient.ttl(key); // Time until key expires
return res.status(429).json({
message: 'Too many requests. Please try again later.',
retryAfter: ttl,
});
}
next(); // Allow request to proceed
} catch (error) {
console.error('Rate limiter error:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
};
};
第四步:创建带速率限制的 API
创建一个 app.ts
文件:
nano app.ts
添加以下代码:
import express, { Request, Response } from 'express';
import { rateLimiter } from './rateLimiter';
const app = express();
const PORT = process.env.PORT || 3000;
// Apply rate limiter middleware
app.use(
rateLimiter({
windowMs: 60 * 1000, // 1 minute window
maxRequests: 10, // Limit each IP to 10 requests per windowMs
})
);
// Example route
app.get('/', (req: Request, res: Response) => {
res.send('Welcome! You are not rate-limited yet.');
});
// Start the server
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
测试应用程序
编译 TypeScript:
npx tsc
运行服务器:
node dist/app.ts
测试速率限制
使用 Postman 或 curl 等工具:
curl http://localhost:3000
- 在 60 秒内发出超过 10 个请求来触发速率限制器。
- 观察 429 Too Many Requests 响应。
生产级注意事项
- 使用安全的 Redis 配置: 确保启用身份验证,并且该实例不会公开。
- 分布式速率限制: 如果应用程序水平扩展,则使用集中的 Redis 实例。
- 日志记录和监控: 集成 Winston 等工具进行日志记录,并集成 Prometheus 进行监控。
结尾
本教程演示了如何使用 Node.js 和 Redis 构建一个健壮的、生产级的速率限制器,以及在现代应用程序平台上部署的步骤。通过遵循这些实践,你可以有效地管理 API 使用并保护你的资源免受滥用。
推广链接: