Skip to content

缓存和限流

更新: 1/16/2025 字数: 0 字 时长: 0 分钟

概述

在NestJS项目中,缓存和限流是两个非常重要的功能。缓存可以提高系统的响应速度,减少数据库的压力;限流可以防止系统被恶意请求攻击,保证系统的稳定性。

缓存

配置

缓存功能依赖于Redis,请确认在上一章中已在环境变量中配置好Redis的连接信息,缓存模块将在src/root.module.ts中初始化Redis连接。

ts
@Module({
	imports: [
        //...
        /**缓存模块 */
        CacheModule.registerAsync({
            isGlobal: true,
            inject: [ConfigService],
            useFactory: async (configService: ConfigService) => {
                const store = await redisStore({
                    password: configService.get('REDIS_PASSWORD', ''),
                    socket: {
                        host: configService.get('REDIS_HOST', 'localhost'),
                        port: configService.get('REDIS_PORT', 6379),
                        passphrase: configService.get('REDIS_PASSWORD', ''),
                    },
                });

                return {
                    store: store as unknown as CacheStore,
                    ttl: 3 * 60000, // 3 minutes (milliseconds)
                };
            },
        })
        //...
    ]
})

使用

在需要使用缓存的接口方法上,使用@CacheTTL()装饰器标记该接口进行缓存。

  • @CacheTTL(毫秒) 参数为毫秒,例如@CacheTTL(1000*60)则表示该接口缓存60秒,60秒内的请求都将从Redis获取并返回缓存数据。
ts
@CacheTTL(1000*60)//缓存60秒
@Get('test-cache')
@ApiOperation({ summary: '缓存http请求,首次请求3秒才返回数据,之后缓存60秒' })
async testCache() {
    return this.redisExampleService.testCache();
}

有时候,我们需要手动移除缓存,则需使用@nestjs/cache-manager提供的CacheManager操作缓存:

ts
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
@Injectable()
export class CacheExampleService {
	constructor(
		@Inject(CACHE_MANAGER) private cacheManager: Cache
	) {}

    /**清除所有缓存 */
    async removeCache() {
		await this.cacheManager.reset();
		return "缓存清除成功";
	}

    /**清除指定key的缓存 */
	async removeCacheByKey(key: string) {
		let cache=`xxxxxx` //默认情况下 key是缓存接口的路径
		await this.cacheManager.del(cache);
		return `${cache}缓存清除成功`;
	}
}

有时候我们需要给同一个接口动态的设置缓存key,以达到不同的效果。情景案例:A,B两个用户访问他们各自的文章列表,假设路径是/post/list,如果没有自定义key,默认的缓存会将第一个用户访问/post/list接口后的信息缓存,第二个用户访问时,将第一个用户的信息当做缓存返回。我们需要给不同的用户设置不同的缓存key。
本项目封装了@CustomCacheKey装饰器,用于动态自定义缓存key:

ts
@CustomCacheKey((context) => {
    const request = context.switchToHttp().getRequest();
    // 获取查询参数
    // const query = request.query;  // ?key=value 形式的参数
    
    // 获取路由参数
    // const params = request.params;  // /:id 形式的参数
    
    // 获取请求体
    // const body = request.body;  // POST 请求的 body
    
    // 获取请求头
    const headers = request.headers;
    
    // 获取请求方法
    // const method = request.method;
    
    // 获取请求路径
    const path = request.path;
    return `${headers.userid}:${path}`;
})
  • @CustomCacheKey的参数是一个方法,方法中持有context请求上下文,可以根据这个context获取想要的信息,并最终拼接出一个自定义的key。
  • 上面的代码示例中,我们假设headers中存在一个userid字段表明当前用户的id,最终拼接${headers.userid}:${path}得到每一个用户不同的key。

提示

  • @CustomCacheKey源码位于src/common/decorators/custom-cache-key.decorator.ts
  • 缓存示例源码位于src/endpoints/app/redis-example

限流

配置

在本项目中使用@nestjs/throttler模块来实现限流功能。

src/endpoints/endpoints.module.ts中配置限流模块:

ts
import { Module } from '@nestjs/common';
import { AppModule } from './app/app.module';
import { AdminModule } from './admin/admin.module';
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';

@Module({
	imports: [
		ThrottlerModule.forRoot([{ 
			ttl: 60000, // 1分钟
			limit: 10,  // 最多10个请求
		}]),
		AppModule,
		AdminModule,
	],
	providers: [ 
		// 如果想开启全局限流 请取消此注释
		// {
		// 	provide: APP_GUARD,
		// 	useClass: ThrottlerGuard,
		// },
	],
})
export class EndpointsModule { }

使用

在控制器中使用限流:

ts
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards } from '@nestjs/common';
import { RateLimitService } from './rate-limit.service';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { SkipThrottle, Throttle, ThrottlerGuard } from '@nestjs/throttler';

@ApiTags('限流用法示例')
@Controller('rate-limit')
export class RateLimitController {
	constructor(private readonly rateLimitService: RateLimitService) { }
	@ApiOperation({ summary: '全局默认限流配置,请连续请求10次测试' })
	@Get('default')
	@UseGuards(ThrottlerGuard) // 如果使用全局限流 此处装饰器可省略
	getDefaultLimit() {
		return this.rateLimitService.getDefaultLimit();
	}

	@ApiOperation({ summary: '自定义限流配置,1分钟最多3次请求,请连续请求3次测试' })
	@Throttle({ default: { limit: 3, ttl: 60000 } })// 用于覆盖ThrottlerModule.forRoot的限流设置
	@UseGuards(ThrottlerGuard) // 如果使用全局限流 此处装饰器可省略
	@Get('strict')
	getStrictLimit() {
		return this.rateLimitService.getStrictLimit();
	}

	@ApiOperation({ summary: '不启用限流' })
	@SkipThrottle() // 如果使用全局限流 此处装饰器可用于跳过限流
	@Get('no-limit')
	getNoLimit() {
		return this.rateLimitService.getNoLimit();
	}
}

提示

  • 限流示例源码位于src/endpoints/app/rate-limit

Released under the MIT License.