跨境Taocarts站点面向全球公网用户,极易遭遇恶意刷接口、爬虫高频请求、CC攻击等问题,高频无效请求会耗尽服务器带宽、数据库资源,导致正常用户接口卡顿、超时。原生系统无任何限流机制,接口完全裸奔,安全性极差。普通的内存限流仅支持单机部署,无法适配Taocarts集群多节点架构,存在限流失效漏洞。本文将基于Redis实现分布式接口限流,支持IP、用户账号双维度限流,支持自定义限流频次,适配集群部署,附带完整可落地代码。
分布式限流核心原理是借助Redis的过期键+自增计数器特性,实现全节点流量统一统计,区别于单机内存限流,集群环境下数据完全互通,不会出现限流阈值失效问题。我们将实现注解式限流,支持接口灵活配置秒级、分钟级访问频次,适配登录、下单、爬虫、查询等不同接口的防护需求。
首先封装Redis限流工具类,实现请求计数、过期设置、频次判断核心逻辑:
typescript
// src/common/utils/redis-limit.util.ts
import { InjectRedis } from '@nestjs/redis';
import { Redis } from 'ioredis';
import { Injectable } from '@nestjs/common';
@Injectable()
export class RedisLimitUtil {
constructor(@InjectRedis() private redis: Redis) {}
/**
* 分布式限流
* @param key 限流唯一key
* @param limit 最大请求次数
* @param second 过期时间(秒)
* @returns true=限流通过 false=触发限流
*/
async limit(key: string, limit: number, second: number): Promise<boolean> {
// 原子自增
const count = await this.redis.incr(key);
// 首次请求设置过期时间
if (count === 1) {
await this.redis.expire(key, second);
}
// 判断是否超过限制频次
return count <= limit;
}
}
自定义限流注解,实现接口灵活配置,无需重复编写限流逻辑:
typescript
// src/common/decorator/rate-limit.decorator.ts
import { SetMetadata } from '@nestjs/common';
export interface RateLimitOption {
limit: number; // 限制次数
second: number; // 限制时间
type: 'ip' | 'user'; // 限流维度
}
export const RateLimit = (options: RateLimitOption) => SetMetadata('rateLimit', options);
编写限流守卫,拦截所有带注解的接口,自动校验请求频次,IP限流适配匿名访问接口,用户限流适配登录后接口:
typescript
// src/common/guard/rate-limit.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { RateLimitOption } from '../decorator/rate-limit.decorator';
import { RedisLimitUtil } from '../utils/redis-limit.util';
import { BusinessException, ErrorCode } from '../exception/business.exception';
@Injectable()
export class RateLimitGuard implements CanActivate {
constructor(private reflector: Reflector, private redisLimit: RedisLimitUtil) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const options = this.reflector.get<RateLimitOption>('rateLimit', context.getHandler());
if (!options) return true;
const req = context.switchToHttp().getRequest();
// 生成唯一限流KEY
let key = `rate_limit:${context.getClass().name}:${context.getHandler().name}`;
if (options.type === 'ip') {
key += `:ip:${req.ip}`;
} else {
key += `:user:${req.user?.userId}`;
}
// 校验限流
const pass = await this.redisLimit.limit(key, options.limit, options.second);
if (!pass) {
throw new BusinessException(ErrorCode.COMMON_ERROR, '请求过于频繁,请稍后再试');
}
return true;
}
}
接口使用示例,给商品爬虫、登录、查询接口添加限流防护:
typescript
@Controller('goods')
export class GoodsController {
// IP限流:10秒最多访问5次,防爬虫刷取商品接口
@Get('list')
@UseGuards(RateLimitGuard)
@RateLimit({ limit: 5, second: 10, type: 'ip' })
async getGoodsList() {
return await this.goodsService.getList();
}
// 用户限流:1分钟最多下单3次,防恶意刷单
@Post('order/create')
@UseGuards(RateLimitGuard)
@RateLimit({ limit: 3, second: 60, type: 'user' })
async createOrder() {
// 下单逻辑
}
}
该限流方案完美适配Taocarts集群部署架构,所有服务器节点共享Redis限流数据,无单点失效问题。可针对不同接口精细化配置防护规则,有效拦截恶意刷接口、爬虫高频抓取、恶意刷单等行为,保护服务器与数据库资源,大幅提升跨境站点抗攻击能力,是商用跨境系统必备的安全防护方案。