Ограничение скорости — важный метод разработки API, позволяющий предотвратить злоупотребления, защитить ресурсы и поддерживать политику добросовестного использования. В этой статье мы рассмотрим различные методы реализации ограничения скорости в NestJS, популярной среде для создания масштабируемых и эффективных серверных приложений с использованием Node.js. Мы обсудим различные подходы и предоставим примеры кода, которые помогут вам понять и эффективно реализовать ограничение скорости в ваших приложениях NestJS.
Содержание:
-
Базовое ограничение скорости с помощью промежуточного программного обеспечения
-
Настраиваемое ограничение скорости с помощью опций
-
Распределенное ограничение скорости с помощью Redis
-
Пользовательские стратегии ограничения скорости
-
Ограничение скорости с помощью пакетных и устойчивых лимитов
-
Ограничение скорости на основе ролей или разрешений пользователей
-
Обработка ошибок превышения лимита скорости
-
Регистрация событий, ограничивающих скорость
-
Ограничение скорости для соединений WebSocket
-
Функциональность ограничения частоты тестирования
-
Базовое ограничение скорости с использованием промежуточного программного обеспечения.
Функции промежуточного программного обеспечения в NestJS предоставляют удобный способ реализации ограничения скорости. Мы можем создать собственное промежуточное программное обеспечение, которое проверяет количество запросов, сделанных клиентом, и применяет ограничение на основе заранее определенного порога. Вот пример:
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class RateLimitMiddleware implements NestMiddleware {
private readonly MAX_REQUESTS = 100;
private requests = {};
use(req: Request, res: Response, next: NextFunction) {
const ip = req.ip;
this.requests[ip] = this.requests[ip] || 0;
if (this.requests[ip] >= this.MAX_REQUESTS) {
return res.status(429).send('Too Many Requests');
}
this.requests[ip]++;
next();
}
}
Чтобы применить промежуточное программное обеспечение к определенному маршруту или ко всему приложению, используйте метод app.use()или apply()в основном файле AppModule.р>
- Настраиваемое ограничение скорости с помощью параметров.
Помимо фиксированного порога запроса часто бывает полезно предоставить настраиваемые параметры ограничения скорости. Мы можем добиться этого, создав собственный декоратор и используя его вместе с перехватчиками NestJS. Вот пример:
import { SetMetadata, createParamDecorator, ExecutionContext } from '@nestjs/common';
export const RateLimit = (limit: number) => SetMetadata('rateLimit', limit);
export const RateLimitInterceptor = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.rateLimit;
},
);
Чтобы использовать декоратор, примените его к определенному обработчику маршрута или методу контроллера и укажите желаемое ограничение скорости в качестве аргумента:
@Get('users')
@RateLimit(100) // Set rate limit to 100 requests per minute
async getUsers(@RateLimitInterceptor() rateLimit: number) {
// Rate limit value can be used in the handler logic
// ...
}
- Распределенное ограничение скорости с использованием Redis.
В распределенных системах ограничение скорости может оказаться затруднительным, поскольку нам необходимо синхронизировать количество запросов между несколькими экземплярами. Используя Redis, мы можем создать масштабируемую и распределенную систему ограничения скорости. Вот пример:
import { Injectable, NestMiddleware } from '@nestjs/common';
import * as Redis from 'ioredis';
@Injectable()
export class RateLimitMiddleware implements NestMiddleware {
private readonly MAX_REQUESTS = 100;
private redisClient = new Redis();
async use(req: Request, res: Response, next: NextFunction) {
const ip = req.ip;
const currentRequests = await this.redisClient.incr(ip);
if (currentRequests >= this.MAX_REQUESTS) {
return res.status(429).send('Too Many Requests');
}
next();
}
}
- Настраиваемые стратегии ограничения скорости:
NestJS позволяет нам создавать собственные стратегии ограничения скорости на основе конкретных требований. Мы можем реализовать различные алгоритмы, такие как корзина токенов, дырявая корзина или скользящее окно, чтобы контролировать скорость входящих запросов. Вот пример алгоритма сегмента токенов:
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class TokenBucketMiddleware implements NestMiddleware {
private readonly MAX_REQUESTS = 100;
private tokens = this.MAX_REQUESTS;
private lastUpdated = Date.now();
async use(req: Request, res: Response, next: NextFunction) {
const currentTime = Date.now();
const elapsedTime = currentTime - this.lastUpdated;
this.tokens += (elapsedTime / 1000) * (this.MAX_REQUESTS / 60);
this.tokens = Math.min(this.tokens, this.MAX_REQUESTS);
this.lastUpdated = currentTime;
if (this.tokens < 1) {
return res.status(429).send('Too Many Requests');
}
this.tokens--;
next();
}
}
Чтобы использовать это промежуточное программное обеспечение, зарегистрируйте его в своем AppModuleи примените к нужным маршрутам или контроллерам.
- Ограничение скорости с помощью пакетных и устойчивых лимитов.
В некоторых сценариях мы можем захотеть установить разные ограничения для периодов пиковой и устойчивой нагрузки. Пакетный лимит относится к количеству запросов, разрешенных за короткий период времени, тогда как устойчивый лимит относится к количеству запросов, разрешенных в течение более длительного периода. Вот пример:
import { Injectable, NestMiddleware } from '@nestjs/common';
import * as rateLimit from 'express-rate-limit';
@Injectable()
export class BurstAndSustainedLimitMiddleware implements NestMiddleware {
private readonly BURST_REQUESTS = 10;
private readonly SUSTAINED_REQUESTS = 100;
use(req: Request, res: Response, next: NextFunction) {
rateLimit({
windowMs: 60 * 1000, // 1 minute
max: this.BURST_REQUESTS,
headers: false,
})(req, res, () => {
rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: this.SUSTAINED_REQUESTS,
headers: false,
})(req, res, next);
});
}
}
- Ограничение скорости на основе ролей или разрешений пользователей.
В некоторых случаях мы можем захотеть применить ограничение скорости на основе ролей или разрешений пользователей. NestJS предоставляет возможность получать и использовать информацию об аутентифицированном пользователе в промежуточном программном обеспечении. Вот пример:
import { Injectable, NestMiddleware } from '@nestjs/common';
import { RequestWithUser } from './interfaces/request-with-user.interface';
@Injectable()
export class RoleBasedRateLimitMiddleware implements NestMiddleware {
private readonly ADMIN_LIMIT = 1000;
private readonly USER_LIMIT = 100;
use(req: RequestWithUser, res: Response, next: NextFunction) {
const rateLimit = req.user.isAdmin ? this.ADMIN_LIMIT : this.USER_LIMIT;
// Apply rate limit logic based on user role
// ...
next();
}
}
- Обработка ошибок превышения лимита скорости.
При превышении лимита скорости важно корректно обработать ошибку и предоставить клиенту соответствующую обратную связь. NestJS позволяет нам создать собственный фильтр исключений для обработки ошибок превышения предела скорости. Вот пример:
import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
@Catch(RateLimitExceededError)
export class RateLimitExceededFilter implements ExceptionFilter {
catch(exception: RateLimitExceededError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
response.status(429).json({
statusCode: 429,
message: 'Rate limit exceeded. Please try again later.',
timestamp: new Date().toISOString(),
});
}
}
Чтобы использовать этот фильтр, примените его глобально или на уровне контроллера.
- Регистрация событий, ограничивающих скорость:
Регистрация событий, ограничивающих скорость, может быть полезна для целей мониторинга и анализа. Мы можем создать собственный перехватчик NestJS для регистрации информации, связанной с ограничением скорости. Вот пример:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Logger } from 'nestjs-pino';
@Injectable()
export class RateLimitLoggingInterceptor implements NestInterceptor {
constructor(private readonly logger: Logger) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { ip, originalUrl } = request;
return next.handle().pipe(
tap(() => {
this.logger.log(`Rate limit: ${ip} - ${originalUrl}`);
}),
);
}
}
Не забудьте зарегистрировать перехватчик в своем AppModule.
- Ограничение скорости для соединений WebSocket:
Ограничение скорости также можно применить к соединениям WebSocket в NestJS. Мы можем создать собственное промежуточное программное обеспечение, которое отслеживает количество подключений WebSocket и применяет ограничение на основе заранее определенного порога. Вот пример:
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Server } from 'socket.io';
@Injectable()
export class WebSocketRateLimitMiddleware implements NestMiddleware {
private readonly MAX_CONNECTIONS = 100;
private connections = 0;
use(socket: Socket, next: (err?: any) => void) {
if (this.connections >= this.MAX_CONNECTIONS) {
return next(new Error('Too Many Connections'));
}
this.connections++;
socket.on('disconnect', () => {
this.connections--;
});
next();
}
}
Чтобы использовать это промежуточное программное обеспечение, зарегистрируйте его в своем AppModuleи примените к серверу WebSocket