在nestjs中,提供了缓存机制,但是数据都储存在内存中的,如果服务器宕机,那么这些数据将会丢失。所以就需要redis这种数据库存储来保存这些数据。
下面我们就来看看如何在nestjs中集成Redis并配合cache-manager来存储数据吧。
Redis
Redis 不是简单的缓存工具,而是实时数据处理的基础平台。不仅是键值存储,更是一个支持多种数据结构的高级内存数据库,被广泛用于缓存、消息代理和实时分析等场景。
- 持久化存储能力
- Redis 提供 RDB(在指定的时间间隔内创建数据快照)和 AOF(记录每个写操作,重启时重新执行这些操作)两种持久化机制
- 即使服务器重启或应用崩溃,缓存数据也不会丢失
- 支持数据备份和恢复,确保业务连续性
- 分布式架构支持
- 在微服务架构中,多个服务实例可以共享同一个 Redis 实例或集群
- 实现真正的全局缓存,避免数据不一致问题
- 支持横向扩展,可以构建 Redis 集群应对高并发场景
- 高性能内存数据库
- 基于内存的操作,读写延迟通常在微秒级别
- 单个 Redis 实例可以处理每秒数十万次的读写操作
- 支持管道(pipelining)批量操作,进一步提升性能
- 丰富的数据结构支持
- 字符串(Strings):基本的键值对存储
- 哈希(Hashes):存储对象,类似 Map 结构
- 列表(Lists):实现队列和栈等数据结构
- 集合(Sets):无序不重复元素集合
- 有序集合(Sorted Sets):带分数的集合
- 位图(Bitmaps)和 HyperLogLogs 等高级结构
- 高级缓存策略
- LRU(最近最少使用)和 LFU(最不经常使用)淘汰策略
- TTL(Time To Live)自动过期机制
- 发布/订阅模式实现事件通知
nestjs集成Redis
我们需要先安装依赖库
npm install --save @nestjs-modules/ioredis ioredis
redis服务,我们通过docker进行管理。这里提供一个docker-compose.yml
version: '3.3'
services:
redis:
image: redis:latest
container_name: redis
restart: always
volumes:
- redis_volume_data:/data
ports:
- 6379:6379
volumes:
redis_volume_data:
启动后,我们可以通过Another Redis Desktop Manager来链接Redis进行数据管理。
导入模块
import { Module } from '@nestjs/common';
import { RedisModule } from '@nestjs-modules/ioredis';
import { AppController } from './app.controller';
@Module({
imports: [
RedisModule.forRootAsync({
useFactory: () => ({
type: 'single',
url: 'redis://localhost:6379',
}),
}),
],
controllers: [AppController],
})
export class AppModule {}
使用
constructor(
@InjectRedis() private readonly redis: Redis,
) { }
@Get()
async getRedisCache(@Query("token") token: string) {
const redisToken = await this.redis.get("token");
console.log("redis有值吗", redisToken, token)
if (redisToken) return redisToken;
// 设置过期时间
await this.redis.set("token", token, "EX", 60);
return token;
}
Cache Manager 结合 Redis
@nestjs/cache-manager 是 NestJS 官方提供的缓存模块,它基于 cache-manager 库构建,提供了统一的缓存接口。Redis 作为其中一个存储后端,通过keyv-redis、cache-manager-redis-yet 或 cache-manager-ioredis-yet 等适配器与 Cache Manager 集成。 注意cache-manager@6+需要结合keyv-redis集成Redis存储。
# 基础依赖
npm install @nestjs/cache-manager cache-manager
# Redis 存储适配器
npm install @keyv/redis keyv
为了模块清晰,我们这里将封装一个Cache模块。
import { Module } from '@nestjs/common';
import { CacheModule as CacheManagerModule } from '@nestjs/cache-manager';
import { createKeyv } from '@keyv/redis';
import { ConfigService } from '@nestjs/config';
import { REDIS_PORT, REDIS_HOST, CACHE_TTL } from '../consts';
@Module({
imports: [
CacheManagerModule.registerAsync({
isGlobal: true,
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
const redisHost = configService.get(REDIS_HOST);
const redisPort = configService.get(REDIS_PORT);
const ttl = configService.get(CACHE_TTL);
return {
stores: [createKeyv(`redis://${redisHost}:${redisPort}`)],
ttl
};
},
}),
],
})
export class CacheModule { }
使用
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import type { Cache } from 'cache-manager';
constructor(
@Inject(CACHE_MANAGER) private cacheManager: Cache
) { }
@Get()
async getCache(@Query("token") token: string) {
const cacheToken = await this.cacheManager.get("token");
console.log("有值吗", cacheToken, token)
if (cacheToken) return cacheToken;
await this.cacheManager.set("token", token);
return token;
}
cacheManager还提供了很多装饰器供我们来处理接口的缓存。
UseInterceptors设置缓存,可以设置全局,控制器,方法级别接口缓存。一般只会设置控制器和方法,不会设置全局。
import { UseInterceptors } from '@nestjs/common';
import {
CacheInterceptor
} from '@nestjs/cache-manager';
@UseInterceptors(CacheInterceptor)
CacheKey为缓存项生成唯一标识符,一般设置方法级别接口缓存。 当没有使用@CacheKey装饰器时,NestJS 会按照以下规则生成缓存键:
[HTTP_METHOD]_[ROUTE_PATH]_[REQUEST_PARAMETERS]
为了更好的控制,所以一般都是我们自己去设置规则生成。例如<模块>:<实体>:<唯一标识>[:<后缀>]。需要根据不同请求参数动态设置标识。
// 静态键
@Get()
@CacheKey('ALL_PRODUCTS')
@CacheTTL(30)
findAll() {
return this.productsService.findAll();
}
// 动态键
// params参数
@Get(':id')
@CacheKey((req) => `PRODUCT_${req.params.id}`)
findOne(@Param('id') id: string) {
return this.productsService.findOne(id);
}
// query参数
@Get('search')
@CacheKey((req) => {
const { q, page = '1' } = req.query;
return `SEARCH_${q}_P${page}`;
})
search(@Query() query: { q: string; page?: string }) {
return this.productsService.search(query);
}
CacheTTL设置缓存过期时间,毫秒。覆盖全局设置的ttl。
@Get()
@CacheKey('empty')
@CacheTTL(60 * 1000)
async findAll(): Promise<number> {
console.log("缓存", this.a, new Date().toLocaleTimeString())
return this.a++;
}
@Get(':id')
@CacheTTL(30 * 1000) // 30秒缓存
findOne(@Param('id') id: string): string {
console.log(`请求参数id ${id}`)
return `请求参数id ${id}`
}
往期年度总结
- 在上海的忙碌一年,依旧充满憧憬(2024)
- 四年沿海城市,刚毕业,一年3家公司
- 七月仿佛又回到了那一年(2023年中总结)
- 一位初入职场前端仔的年度终结 <回顾2022,展望2023>
- 大学两年半的前端学习
往期文章
- 脚手架开发之多包管理(npm, yarn, pnpm workspaces)
- 告别后端,浏览器端也可以轻松操作文件了?
- 文本选区有啥魔力
- 前端可以知道的录制浏览器标签页,没有黑魔法
- 一个关联本地页面镜像的功能,我了解到这些
- 啊,原来sessionStorage是这样的
- 如何像掘金编辑器一样粘贴图片即可上传服务器
- Nest装饰器全解析
- Nest世界中的AOP
- Nestjs如何解析http传输的数据
- 如何理解js的DOM事件系统
- 半年没看vue官网,3.5刚刚发布,趁机整理下
- 啊,你还在找一款强大的表格组件吗?
- 前端大量数据层级展示及搜索定位预览
- 如何从0开始认识m3u8(提取,解析及下载)
- 展示大量数据节点(tree),引发的一次性能排查
- ts装饰器的那点东西
- 这是你所知道的ts类型断言和类型守卫吗?
- TypeScript官网内容解读
- 经常使用ts的你,知道这些内容?
- 你有了解过原生css的scope?
- 现在比较常用的移动端调试你知道哪些?
- 众多跨标签页通信方式,你知道哪些?(二)
- 众多跨标签页通信方式,你知道哪些?
- 反调试吗?如何监听devtools的打开与关闭
- 因为原生,选择一家公司(前端如何防笔试作弊)
- 结合开发,带你熟悉package.json与tsconfig.json配置
- 如何优雅的在项目中使用echarts
- 如何优雅的做项目国际化
- 近三个月的排错,原来的憧憬消失喽
- 带你从0开始了解vue3核心(运行时)
- 带你从0开始了解vue3核心(computed, watch)
- 带你从0开始了解vue3核心(响应式)
- 3w+字的后台管理通用功能解决方案送给你
- 入职之前,狂补技术,4w字的前端技术解决方案送给你(vue3 + vite )
专栏文章
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论, 支持一下博主~
公众号:全栈追逐者,不定期的更新内容,关注不错过哦!