在 NestJs 中封装一个通用的 redis 服务,之后可以直接复制这套服务了

文章开始之前分享两个开源项目,会一直维护的,欢迎 star,如果你感兴趣或者想参与学习,可以加我微信 yunmz777,最近也在找工作 ing,欢迎内推......

浪费你两秒钟时间,我们正文开始!!!

Redis 是一个开源的、高性能的键值数据库,通常用作数据结构服务器。它支持多种类型的数据结构,如字符串(strings)、列表(lists)、集合(sets)、有序集合(sorted sets)、哈希表(hashes)、位图(bitmaps)、超日志(hyperloglogs)和地理空间(geospatial)索引。Redis 的主要特点是它的数据都保存在内存中,从而提供极高的读写速度,同时它也支持将数据异步写入磁盘以进行持久化。

它的主要使用场景有以下几个方面:

  1. 缓存系统:利用其高速的数据读写能力,常用作数据库缓存,减少数据库负载,提高系统响应速度。

  2. 会话存储(Session Store):存储用户会话信息,由于其速度快,适合此类需求。

  3. 消息队列系统:利用其发布/订阅系统实现消息队列,支持实时消息系统。

  4. 实时计数器:如计数网站点击量或在社交媒体上跟踪帖子的点赞数。

  5. 排行榜/计分板:利用 Redis 的有序集合,非常适合需要排名的功能,如游戏排行榜。

在接下来的内容中我们将使用 NestJs 对其进行封装。

使用 docker-compose 创建一个 redis 服务

接下来我们将创建一个简单的 redis 服务,在 docker-compose.yml 文件中编写如下代码:

yml 复制代码
version: "3.9"

services:
  redis:
    image: redis
    ports:
      - "6379:6379"
    environment:
      - REDIS_PASSWORD=moment
    command: redis-server --requirepass moment
    volumes:
      - redis-data:/data
    restart: always

volumes:
  redis-data:

在上面的配置中,通过 Redis 服务的命令行参数 --requirepass 设置密码。通过定义一个 Docker 卷 redis_data,并将其挂载到容器的 /data 目录,从而使 Redis 数据在容器重启后仍然保持。

使用 docker-compose up -d 命令之后我们就可以连接了:

设置环境变量

这个时候我们的 redis 服务已经创建完成了,我们需要在 env 文件下新增一些环境变量,如下所示:

env 复制代码
REDIS_HOST = localhost
REDIS_PORT = 6379
REDIS_PASSWORD = moment
REDIS_DB = 0

创建完成之后我们要创建一个枚举变量:

ts 复制代码
export enum RedisConfigEnum {
  REDIS_HOST = "REDIS_HOST",
  REDIS_PORT = "REDIS_PORT",
  REDIS_PASSWORD = "REDIS_PASSWORD",
  REDIS_DB = "REDIS_DB",
}

我们新建一个配置文件,编写一个函数并返回一个 redis 的配置对象:

ts 复制代码
import { ConfigService } from "@nestjs/config";

import { RedisConfigEnum } from "../common/enum/config.enum";

export default (configService: ConfigService) => ({
  port: parseInt(configService.get(RedisConfigEnum.REDIS_PORT)),
  host: configService.get(RedisConfigEnum.REDIS_HOST),
  password: configService.get(RedisConfigEnum.REDIS_PASSWORD),
  db: configService.get(RedisConfigEnum.REDIS_DB),
});

这个时候我们就可以编写我们的 redis 模块了,这个时候我们只需要这两个文件:

redis 模块配置

首先我们编写一下 redis.module.ts 文件,如下代码所示:

ts 复制代码
import { Global, Module } from "@nestjs/common";
import Redis from "ioredis";
import { ConfigService } from "@nestjs/config";

import loadRedisConfig from "../../config/redis.config";

import { RedisService } from "./redis.service";

@Global()
@Module({
  providers: [
    {
      provide: "REDIS_CLIENT",
      async useFactory(configService: ConfigService) {
        const redisInstance = new Redis({
          ...loadRedisConfig(configService),
        });
        return redisInstance;
      },
      inject: [ConfigService],
    },
    RedisService,
  ],
  exports: [RedisService],
})
export class RedisModule {}

要想使用,我们首先需要安装 ioredis 这个模块:

bash 复制代码
pnpm add ioredis

在上面的代码中,我们使用 RedisModule 为整个应用提供了集中式的 Redis 客户端管理,便于在任何需要的地方通过依赖注入使用 Redis。这种模式提高了代码的模块化和可重用性,同时也简化了配置管理和维护的复杂性。

redis 服务

在前面我们已经为 redis 模块中,在 providers 中包含 RedisService 以确保可以在模块中使用。

那么在我们的服务中就可以使用 @Inject('REDIS_CLIENT') 将 Redis 客户端实例注入到服务中。这样,服务内的各个方法就可以使用这个 Redis 客户端与 Redis 数据库进行交互

并且定义了一些方法来对 redis 进行封装:

  1. onModuleDestroy():在模块被销毁时调用,用于断开与 Redis 的连接。这是 OnModuleDestroy 生命周期钩子的实现,确保应用停止时清理资源。

  2. set():

    • 重载方法,用于设置键值对。如果提供了 second 参数,键将在指定的秒数后过期。

    • 使用 isObject() 检查值是否为对象,如果是,则将对象转换为 JSON 字符串存储;否则直接存储值。

    • 如果没有提供过期时间,则只调用 set(key, value);如果提供了过期时间,则使用 set(key, value, 'EX', second),其中 'EX' 指定过期时间单位为秒。

  3. incr():对指定键执行自增操作,并返回新的值。这适用于计数器等功能。

  4. get():

    • 获取指定键的值。尝试对获取的值进行 JSON 解析,如果解析成功则返回解析后的对象,否则直接返回字符串。

    • 如果键不存在或在获取过程中发生错误,则返回 null。

  5. del():删除指定的键,并返回删除的键的数量。

  6. hset():向哈希表中添加字段,field 参数应为一个对象,其中包含要设置的键值对。返回被成功设置的字段数量。

  7. hget():从哈希表中获取指定字段的值。

  8. hgetall():获取哈希表中所有的字段和值。

  9. lushall():清空 Redis 数据库中的所有数据。

通过上面的内容我们就已经对 redis 进行了一个比较完整的封装了,那么接下来我们将对 redis 进行一个简单的测试。

使用案例

首先我们需要在使用的模块中导入或者你也可以将其注册为全局模块,目前我们在 moment 模块中使用:

之后我们在 moment.service.ts 中编写如下代码:

ts 复制代码
import { Injectable } from "@nestjs/common";

import { RedisService } from "@/common/redis/redis.service";

@Injectable()
export class MomentService {
  constructor(private readonly redisService: RedisService) {}

  async create() {
    const data = await this.redisService.set("moment", 777);

    return data;
  }
}

在 moment.controller 中编写如下代码:

ts 复制代码
import { Controller, Get } from "@nestjs/common";

import { MomentService } from "./moment.service";

@Controller("moment")
export class MomentController {
  constructor(private readonly momentService: MomentService) {}

  @Get()
  create() {
    return this.momentService.create();
  }
}

这个时候我们就可以测试一下我们封装的 redis 服务是否生效了,我们访问 moment 接口 :

打开数据库,我们可以看到我们的 redis 服务是封装成功了的:

总结

我们通过封装 Redis 操作到一个服务中可以集中管理所有与 Redis 交互的逻辑。这样做使得维护和更新变得更加容易,因为所有的 Redis 相关代码都位于一个位置,而不是散布在整个应用的多个部分。

并且通过服务封装,你可以在多个组件之间复用 Redis 操作代码,避免重复代码。例如,不同的控制器或服务可能需要访问相同的数据,通过依赖注入 RedisService,它们可以轻松地复用已经定义好的方法,如 get, set, incr 等。

相关推荐
zm13 分钟前
服务器连接多客户端
java·javascript·算法
jllllyuz37 分钟前
matlab实现蚁群算法解决公交车路径规划问题
服务器·前端·数据库
Warren981 小时前
Java面试八股Spring篇(4500字)
java·开发语言·spring boot·后端·spring·面试
背帆1 小时前
go的interface接口底层实现
开发语言·后端·golang
小屁孩大帅-杨一凡1 小时前
一个简单点的js的h5页面实现地铁快跑的小游戏
开发语言·前端·javascript·css·html
读心悦1 小时前
CSS 布局系统深度解析:从传统到现代的布局方案
前端·css
椒盐螺丝钉2 小时前
CSS盒子模型:Padding与Margin的适用场景与注意事项
前端·css
IT成长史2 小时前
deepseek梳理java高级开发工程师springboot面试题2
java·spring boot·后端
qq_266348732 小时前
springboot AOP中,通过解析SpEL 表达式动态获取参数值
java·spring boot·后端
萧鼎2 小时前
构建全栈 Web 应用的新选择:NextPy 技术详解与实战指南
前端