2024年你应该如何在NestJS中使用Redis

622 阅读5分钟

说实话,这篇文章可以被列进我最不想写的文章TOP3,因为写这种在XX中使用XX的人实在太多了,如果你想知道如何在XX中使用XX,不用翻看自己的文章,直接网络上搜索,多得是。但我为什么最终还是决定把这个问题记录下来,因为NestJS这玩意实在太扯淡了。

我曾经三次参考网上教程将Redis引入NestJS,三次都花了很长时间,其中有很大一部分原因不知道是版本问题还是其他别的什么问题,他们写的方法,我照着写会报错,我可去他的吧。而且如果有耐心搜过这玩意的同学会发现,古今中外有不少人试图用不同的方法不同的npm包去实现这件事并留下了各种一样或不一样的记录,stack overflow也有很多在尝试其他人方法时遇到我一样报错人的解决办法,总而言之,他们的方法太不靠谱,成功率也太低了。

关于使用Redis我曾经有两种构想,一种是将原本的Cache源替换,兼容老的cache相关方法,另一种是引入新的redis方法,新老互不影响。前几次我都是采用第一种方案,碰了不少壁,虽然成功了,但代价巨大,这一次我决定使用第二种。不过如果想用第一种方案的同学也别着急退出,也许这篇文章多少会对你有点启发。

那就正式开始了。

一、别人怎么说的

熟悉我的人都知道,我想知道一个功能怎么做,第一反应是去翻官方文档,关于如何引入Redis,官方有两种方式,一种是直接替换,一种是使用微服务。

image.png

直接替换的情况下,官方推荐的是cache-manager-redis-store,也怪我没看清楚,文档下面用黄字标注了,这个包只适用于 3.xx版本,而我们公司的Redis买的是 7.xx 版本

image.png

不过好在引入的时候也报错了,类型不匹配

screenshot-20241009-142544.png

至于微服务版本就不用说了,不太会考虑这种引入方式

image.png

既然官方文档行不通,我便开始寻找一些路人文章,这里我按日期找了一位看上去还比较靠谱的大哥

image.png

但是直接在第一步下载依赖就给我卡住了

image.png

@nestjs/redis包在npm仓库根本找不到,当然,我也尝试过将他下面使用到的一些包直接找到对应依赖下载,依然跑不通,感兴趣的同学可以进到 原文 自行体验。

二、比较可靠的方案

cache-manager-ioredis 包是可以直接引用的,在注册缓存时可以将Redis引入进去实现前面提到的第一种构想。

import * as redisStore from 'cache-manager-ioredis';
CacheModule.register({ 
    store: redisStore, 
    host: 'redis地址', 
    port: 6379, 
    password: 'YImu654321', 
    isGlobal: true, 
    db: 0, 
    ttl: 3600 * 24 
 }),

但是这里并不是想讲这种方法,所以一笔带过。

在更早之前,我找到过一个类似包,叫cache-manager-redis-yet,是cache-manager-redis-store引入报错时从stack overflow的一篇解答里推荐的,这次我想重新试试这个包好不好用,但发现npm中找不到了

image.png

通过地址栏进去之后会发现它被弃用了,最后一次推送是17天前

image.png

image.png

要知道, cache-manager-ioredis 的上次推送时间是4年前

image.png

这就让我对弃用信息里提到的keyv产生了兴趣,毕竟它的上次推送时间是3天前,周下载量是三千两百万

image.png

三、keyv

keyv的使用方式非常简单,详情可以参考 官方npm文档

引入:

npm install --save keyv
npm install --save @keyv/redis

使用:

import Keyv from 'keyv';
import KeyvRedis from '@keyv/redis';

const keyvRedis = new KeyvRedis('redis://user:pass@localhost:6379');
const keyv = new Keyv({ store: keyvRedis });

await keyv.set('foo', 'expires in 1 second', 1000); // true
await keyv.set('foo', 'never expires'); // true
await keyv.get('foo'); // 'never expires'
await keyv.delete('foo'); // true
await keyv.clear(); // undefined

但是,由于众所周知的原因,我们不可能每次需要使用redis的时候都去重新连接一次,所以我们一般会用注册的方式引入

import { Module, Global } from '@nestjs/common';  
import Keyv from 'keyv';  
import KeyvRedis from '@keyv/redis';

@Global() // 可选:如果你希望这个模块在整个应用中可用
@Module({
  providers: [
    {
      provide: 'CACHE',  
      useFactory: () => {
        const keyv = new Keyv({ store: new KeyvRedis("redis://:密码@redis地址:6379/0") }); // 可以根据你的配置选择存储
        return keyv;  
      },
    },
  ],
  exports: ['CACHE'],
})
export class RedisModule {}

其中6379对应的是redis的端口号,/0对应的是redis的字典库,redis默认有16个库,分别是db0~db15,不同db之间数据不共通,可以用于区分环境,但需要注意的是集群redis可能只有db0。

引入方式也很简单,这里就不贴代码了,用过NestJS的小伙伴对这种引用应该已经很熟悉了,注意这里的 CACHE 要和上面填写的的provide和exports相对应。

image.png

如果想设置过期时间,可以在set时添加:

await this.cacheServer.set("test", "测试存放内容", 60 * 1000);

对应的时间单位是毫秒。

添加完成后可以在redis客户端找到相应的缓存数据:

image.png

四、拓展

如果你有在关注cache-manage 这个包的话,你会发现,它的 npm文档 近期也有更新,不难看出keyv将会是后续nestjs cache使用上比较主流的方式,由于我的项目目前使用的版本号有点老旧,更新稍微有点困难,就不做尝试了,有兴趣的小伙伴可以安装新版本体验一下。

image.png