Skip to main content

Redis 驱动

npm地址

背景闲聊

本插件最早起于nestjs-redis库在 NestJS 8.x 版本下不好使,同时又迟迟等不到更新,其他的解决方案下载量又不够大的情况下迫不得已自己写了一套,当时主要参考@liaoliaots/nestjs-redis这个包。其实在当时我是没有勇气写一个这么基础、这么通用的包的,因为这种属于生态的基石类的库肯定是有成熟稳定的解决方案的,自研又无法投入大量的时间精力来慢慢打磨。最终还是咬牙写了下来的主要原因是在当时@liaoliaots/nestjs-redis这个包的下载了还没有上来,不敢直接使用。现在看来这个包的下载量已经上来了,但是我们自研的@iot9x.com/nestjs-redis也走出了自己的特色,就继续在自研的道路上走下去吧。

设计说明

自研的道路肯定是以满足自身业务发展为前提的,目前我们的 redis 还没有到上集群的规模,因此此包专注于单机 Redis 。目前的 nestjs-redis 解决方案都是基于底层ioredisredis的,通过依赖注入得到一个 ioredisredis 的客户端,接下来的各种操作都是 ioredis/redis 的客户端提供的接口了。

提示

关于 ioredisredis 的具体区别不在本文档的讨论范围内,有兴趣可以自行研究。

本插件选择将 ioredis 作为底层驱动。

特色功能

RedisService

由于市场上的解决方案都是通过依赖注入得到一个 ioredis/redis 的客户端,但是这个客户端的操作返回都是比较原始的,基本上和使用 redis-cli 的效果一致。那就会遇到一个不够友好的情况,就是我们得到的数据很多情况下要做进一步的处理。

例如,通过 set 方法往数据库中插入数据,如果成功,返回的是一个 Promise<"OK"> ,那要判断插入是否成功了,就要通过判断 result === "OK" 来实现了。众所周知,在代码中写死字符串不是一个好习惯,我们更希望得到一个 boolean 类型的值来代表执行成功与否。

还有,有的时候会将 DateNumberObjectArray 类型的数据插入数据库,在某些环境下会直接将其序列化为 string 后作为 value 插入数据库。

提示

对于 ObjectArray 使用更加具体的散列表集合链表等方式无疑是更合适的,这里考虑的是“偷懒”的情况。

这种情况下,我们通过 get 得到的数据还要继续通过 JSON.parse(data) 来转换回来,无疑增加了代码量。我们更希望借助 TS 的泛型来实现这种指定类型的方式,如下所示:

const data = await this.redisService.get<Date>("redis_key");

基于以上几点,我封装了 RedisService ,并通过 @InjectRedisService() 装饰器来注入,可以实现更便捷的数据库操作方式。

由于 NodeJS 是单主线程的,我们在线上为了利用多核性能与集群,一般会部署多个实例。但是部署多个实例的时候就会遇到资源竞争问题,最常见的就是定时计划任务。例如:每天凌晨2点执行不重要的日志清理,这个时候如若不加限制,那就会造成每个实例都执行一次计划任务。为了这种简单的资源竞争问题,基于 RedisSetNX 实现了一个简易的锁,具体详见文档——