将 ioredis 封装为 NestJS 专用的 nest-redis 模块
在开发 NestJS 应用时,我们通常会使用 ioredis 作为 Redis 客户端。为了更好地集成 ioredis,我们可以将其封装为一个 NestJS 模块,称为 nest-redis 模块。 该模块可以支持同步和异步两种配置方式,分别通过 forRoot 和 forRootAsync 方法实现。
模块实现步骤
1. 定义 RedisModule 类:
使用 @Global 和 @Module 装饰器标识 RedisModule 类。该类包含两个方法:forRoot 和 forRootAsync,用于接收配置参数并返回相应的 DynamicModule 实例
2. 拓展ioredis默认参数
forRoot 方法接收 RedisModuleOptions 参数,返回类型为 DynamicModule。该参数可以扩展 ioredis 的默认参数。
import { ModuleMetadata } from '@nestjs/common';
import { RedisOptions } from 'ioredis';
export interface RedisModuleOptions extends RedisOptions {}
export interface RedisModuleAsyncOptions
extends Pick<ModuleMetadata, 'imports'> {
useFactory?: (
...args: any[]
) => Promise<RedisModuleOptions> | RedisModuleOptions;
inject?: any[];
}
3. forRoot 方法:
查看DynamicModule得知需要返回一个对象,必须有属性module,值为模块参考。返回一个Provider类型的对象,属性provide为自定义标识符,useValue为实例提供者
@Global()
@Module()
export class RedisModule {
static forRoot(options: RedisModuleOptions): DynamicModule {
const redisProvider: Provider = {
provide: REDIS_CLIENTS,
useValue: new Redis(options),
};
return {
module: RedisModule,
providers: [redisProvider],
exports: [redisProvider],
};
}
}
4. forRootAsync 方法:
forRootAsync 方法使用 useFactory 模式,异步返回一个 Redis 实例配置。
static forRootAsync(options: RedisModuleAsyncOptions): DynamicModule {
const { imports, inject, useFactory } = options;
const redisProvider: Provider = {
provide: REDIS_CLIENTS,
useFactory: async (...args): Promise<Redis> => {
const redisOptions: RedisOptions = await useFactory(...args);
const redisClient = await new Redis(redisOptions);
return redisClient;
},
inject,
};
return {
module: RedisModule,
imports,
providers: [redisProvider],
exports: [redisProvider],
};
}
5. 定义redis注入装饰器
REDIS_CLIENTS 为自定义标识符,需要和上面定义provide属性的是同一个值
import { Inject } from '@nestjs/common';
import { REDIS_CLIENTS } from './redis.contanst';
export const InjectRedis = () => {
return Inject(REDIS_CLIENTS);
};
6. 二次封装ioredis的方法
import { Inject, Injectable } from '@nestjs/common';
import { REDIS_CLIENTS } from './redis.contanst';
import Redis from 'ioredis';
@Injectable()
export class RedisService {
private redisClient: Redis;
constructor(@Inject(REDIS_CLIENTS) redis: Redis) {
this.redisClient = redis;
}
async get(key: string) {
return this.redisClient.get(key);
}
async set(key: string, value: number | string, ttl?: number) {
await this.redisClient.set(key, value);
if (ttl) {
await this.redisClient.expire(key, ttl);
}
}
}
7. 使用示例
- 在appModule 导入RedisModule模块
@Module({
imports: [
// 异步配置
RedisModule.forRootAsync({
useFactory: (config: ConfigService) => {
return config.get('redis');
},
inject: [ConfigService],
}),
// 同步配置
RedisModule.forRoot({
port: 6379,
host: 'localhost',
}),
]
})
- 在需要用到的service中实例化redisServer
import { Injectable } from '@nestjs/common';
import { Redis } from 'ioredis';
import { InjectRedis } from 'plugin/redis';
import { RedisService } from 'plugin/redis/redis.service';
@Injectable()
export class TestRedisService {
constructor(
@InjectRedis() private readonly redisClient: Redis,
private readonly redisService: RedisService,
) {}
async get1(key: string): Promise<string> {
return await this.redisService.get(key);
}
async get2(key: string): Promise<string> {
return await this.redisClient.get(key);
}
}