1. 概述
RedisModule 是 FastbuildAI 后端应用的核心缓存模块,负责配置和管理与 Redis 数据库的连接。该模块使用工厂模式创建 RedisService 实例,提供了完整的 Redis 操作功能,包括基本的键值操作、发布订阅、健康检查等功能。
主要职责
- 配置 Redis 数据库连接
- 提供 Redis 客户端实例管理
- 支持基本的 Redis 操作(GET、SET、DEL等)
- 实现发布订阅功能
- 提供健康检查支持
- 集成到应用的缓存和队列系统中
2. 模块装饰器分析
typescript
// 源码位置:apps/server/src/core/redis/redis.module.ts
@Module({
imports: [],
providers: [
{
provide: RedisService,
useFactory: () => {
return new RedisService();
},
},
],
exports: [RedisService],
})
export class RedisModule {}
配置说明
- providers : 使用工厂模式提供
RedisService实例 - exports : 导出
RedisService供其他模块使用
3. 服务提供者分析
3.1 工厂模式的 providers 配置
typescript
// 源码位置:apps/server/src/core/redis/redis.module.ts
providers: [
{
provide: RedisService,
useFactory: () => {
return new RedisService();
},
},
],
3.2 工厂模式的优势
- 延迟初始化: 在需要时才创建 Redis 连接
- 灵活性: 支持根据配置文件的值来初始化 Redis 连接参数
4. 工厂函数详解
4.1 useFactory 的实现机制
typescript
// 源码位置:apps/server/src/core/redis/redis.module.ts
useFactory: () => {
return new RedisService();
}
4.2 工厂函数特点
- 实例创建 : 直接创建
RedisService实例,无需依赖注入 - 简化配置: 不依赖 ConfigService,直接使用环境变量
5. 服务导出
5.1 exports 的作用
typescript
// 源码位置:apps/server/src/core/redis/redis.module.ts
exports: [RedisService]
5.2 导出的意义
- 模块间共享: 允许其他模块使用 Redis 服务
- 单例模式: 确保整个应用中只有一个 Redis 服务实例
- 依赖管理: 简化其他模块对 Redis 功能的使用
6. RedisService 实现分析
6.1 服务类结构
typescript
// 源码位置:apps/server/src/core/redis/redis.service.ts
@Injectable()
export class RedisService implements OnModuleDestroy {
private redisClient: RedisClientType;
private isConnected = false;
constructor() {
this.initRedisClient();
}
}
6.2 Redis 客户端初始化
typescript
// 源码位置:apps/server/src/core/redis/redis.service.ts
private async initRedisClient() {
this.redisClient = createClient({
socket: {
host: process.env.REDIS_HOST || "localhost",
port: Number(process.env.REDIS_PORT) || 6379,
},
username: process.env.REDIS_USERNAME || "",
password: process.env.REDIS_PASSWORD || "",
database: Number(process.env.REDIS_DB) || 0,
});
this.redisClient.on("error", (err) => {
TerminalLogger.error("Redis客户端", `错误: ${err}`);
});
this.redisClient.on("connect", () => {
TerminalLogger.success("Redis Status", "Connected");
this.isConnected = true;
});
await this.redisClient.connect();
}
1. 创建 Redis 客户端
typescript
this.redisClient = createClient({
socket: {
host: process.env.REDIS_HOST || "localhost",
port: Number(process.env.REDIS_PORT) || 6379,
},
username: process.env.REDIS_USERNAME || "",
password: process.env.REDIS_PASSWORD || "",
database: Number(process.env.REDIS_DB) || 0,
});
配置参数说明:
-
socket 配置:
host: Redis 服务器地址,从环境变量REDIS_HOST获取,默认为"localhost"port: Redis 服务器端口,从环境变量REDIS_PORT获取,默认为6379
-
认证配置:
username: Redis 用户名,从环境变量REDIS_USERNAME获取,默认为空字符串password: Redis 密码,从环境变量REDIS_PASSWORD获取,默认为空字符串
-
数据库配置:
database: Redis 数据库编号(0-15),从环境变量REDIS_DB获取,默认为0
2. 错误事件监听
typescript
this.redisClient.on("error", (err) => {
TerminalLogger.error("Redis客户端", `错误: ${err}`);
});
- 监听 Redis 客户端的
error事件 - 当发生错误时,使用
TerminalLogger记录错误信息 - 错误信息包含中文标识 "Redis客户端" 和具体错误内容
3. 连接成功事件监听
typescript
this.redisClient.on("connect", () => {
TerminalLogger.success("Redis Status", "Connected");
this.isConnected = true;
});
- 监听 Redis 客户端的
connect事件 - 连接成功时:
- 使用
TerminalLogger.success()输出成功日志 - 设置实例属性
this.isConnected = true,标记连接状态
- 使用
4. 建立连接
typescript
await this.redisClient.connect();
- 异步调用
connect()方法建立实际的 Redis 连接 - 使用
await等待连接完成 - 连接成功后会触发上面注册的
connect事件
5. 设计特点
- 环境变量配置:所有连接参数都支持通过环境变量配置,提供了合理的默认值
- 错误处理:完善的错误监听和日志记录机制
- 状态管理 :通过
isConnected属性跟踪连接状态 - 异步处理 :使用
async/await确保连接建立完成后再继续执行 - 日志输出 :使用自定义的
TerminalLogger提供清晰的状态反馈
6. 使用场景
这个方法通常在服务启动时调用,确保 Redis 客户端正确初始化并建立连接,为后续的缓存操作提供基础支持。
6.3 基本操作方法
typescript
// 源码位置:apps/server/src/core/redis/redis.service.ts
// 获取值
async get<T>(key: string): Promise<T | null> {
return this.redisClient.get(key) as Promise<T | null>;
}
// 设置值
async set(key: string, value: string, ttl?: number): Promise<void> {
if (ttl) {
await this.redisClient.setEx(key, ttl, value);
} else {
await this.redisClient.set(key, value);
}
}
// 删除键
async del(key: string): Promise<void> {
await this.redisClient.del(key);
}
// 重置所有缓存
async reset(): Promise<void> {
await this.redisClient.flushDb();
}
6.4 高级功能
typescript
// 源码位置:apps/server/src/core/redis/redis.service.ts
// 获取Redis客户端实例
getClient(): RedisClientType {
return this.redisClient;
}
// 执行Redis命令
async executeCommand(command: string, ...args: any[]): Promise<any> {
return this.redisClient.sendCommand([command, ...args]);
}
// 发布消息
async publish(channel: string, message: string): Promise<number> {
return this.redisClient.publish(channel, message);
}
// 订阅频道
async subscribe(
channel: string,
callback: (message: string, channel: string) => void,
): Promise<void> {
const subscriber = this.redisClient.duplicate();
await subscriber.connect();
await subscriber.subscribe(channel, (message, channel) => {
callback(message, channel);
});
}
7. 设计模式和优势
7.1 设计模式
- 工厂模式 : 使用
useFactory创建服务实例 - 单例模式: 通过模块导出确保单例
- 环境变量配置: 直接从环境变量读取配置
- 观察者模式: Redis 事件监听机制
7.2 技术优势
- 配置灵活: 支持环境变量配置
- 连接管理: 自动连接和断开管理
- 错误处理: 完善的错误监听和日志记录
- 功能完整: 支持基本操作和高级功能
- 类型安全: TypeScript 类型支持
8. 实际工作流程
8.1 模块启动流程
应用启动 AppModule.register方法执行 导入RedisModule RedisModule装饰器解析 执行useFactory工厂函数 创建RedisService实例 RedisService构造函数执行 调用initRedisClient方法 从环境变量读取配置 创建Redis客户端 注册错误和连接事件监听器 连接Redis服务器 触发connect事件 设置isConnected状态 Redis模块启动完成
8.1.1 详细流程说明
-
应用启动阶段
- NestJS应用启动时,执行
AppModule.register()方法 - 在imports数组中直接导入
RedisModule
- NestJS应用启动时,执行
-
模块初始化阶段
typescript// 源码位置:apps/server/src/modules/app.module.ts imports: [ DatabaseModule, RedisModule, // 直接导入,无需额外配置 CacheModule, // ...其他模块 ] -
服务实例化阶段
typescript// 源码位置:apps/server/src/core/redis/redis.module.ts providers: [ { provide: RedisService, useFactory: () => { return new RedisService(); // 直接创建实例,无需依赖注入 }, }, ] -
Redis客户端初始化阶段
typescript// 源码位置:apps/server/src/core/redis/redis.service.ts constructor() { this.initRedisClient(); // 构造函数中直接调用初始化方法 } -
配置读取和连接建立
- 直接从
process.env读取环境变量配置 - 创建Redis客户端并建立连接
- 注册事件监听器处理连接状态
- 直接从
8.2 服务使用流程
基本操作 高级操作 发布订阅 其他模块需要Redis服务 导入RedisModule 在构造函数中注入RedisService 调用Redis服务方法 操作类型判断 get/set/del/reset executeCommand/getClient publish/subscribe 执行Redis命令 返回操作结果
8.2.1 使用示例
-
模块导入方式
typescript// 在需要使用Redis的模块中 @Module({ imports: [RedisModule], // 导入Redis模块 providers: [SomeService], }) export class SomeModule {} -
服务注入方式
typescript// 在服务中注入RedisService @Injectable() export class SomeService { constructor( private readonly redisService: RedisService, // 直接注入 ) {} } -
方法调用方式
typescript// 基本操作 await this.redisService.set('key', 'value', 3600); const value = await this.redisService.get('key'); await this.redisService.del('key'); // 高级操作 const client = this.redisService.getClient(); await this.redisService.executeCommand('HSET', 'hash', 'field', 'value'); // 发布订阅 await this.redisService.publish('channel', 'message'); await this.redisService.subscribe('channel', (message, channel) => { console.log(`收到消息: ${message}`); });
8.3 配置管理流程
应用启动 env.util.ts加载环境变量 process.env中存储配置 RedisService初始化 从process.env读取配置 应用默认值 创建Redis客户端配置 建立Redis连接
8.3.1 配置优先级
- 环境变量优先: 优先使用环境变量中的配置
- 默认值兜底: 环境变量不存在时使用默认值
- 配置项完整: 所有必要配置都有合理默认值
typescript
// 源码位置:apps/server/src/core/redis/redis.service.ts
this.redisClient = createClient({
socket: {
host: process.env.REDIS_HOST || "localhost", // 默认localhost
port: Number(process.env.REDIS_PORT) || 6379, // 默认6379
},
username: process.env.REDIS_USERNAME || "", // 默认空字符串
password: process.env.REDIS_PASSWORD || "", // 默认空字符串
database: Number(process.env.REDIS_DB) || 0, // 默认数据库0
});
8.4 生命周期管理
模块初始化 RedisService创建 Redis连接建立 服务正常运行 应用关闭信号 onModuleDestroy触发 关闭Redis连接 清理资源 模块销毁完成
8.4.1 优雅关闭机制
typescript
// 源码位置:apps/server/src/core/redis/redis.service.ts
async onModuleDestroy() {
if (this.redisClient && this.isConnected) {
await this.redisClient.quit(); // 优雅关闭连接
this.isConnected = false; // 更新连接状态
TerminalLogger.info("Redis", "连接已关闭"); // 记录关闭日志
}
}
9. 配置参数说明
9.1 环境变量配置
| 变量名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
REDIS_HOST |
string | localhost | Redis 服务器地址 |
REDIS_PORT |
number | 6379 | Redis 服务器端口 |
REDIS_USERNAME |
string | "" | Redis 用户名 |
REDIS_PASSWORD |
string | "" | Redis 密码 |
REDIS_DB |
number | 0 | Redis 数据库编号 |
9.2 Redis 客户端配置
typescript
// 源码位置:apps/server/src/core/redis/redis.service.ts
{
socket: {
host: process.env.REDIS_HOST || "localhost",
port: Number(process.env.REDIS_PORT) || 6379,
},
username: process.env.REDIS_USERNAME || "",
password: process.env.REDIS_PASSWORD || "",
database: Number(process.env.REDIS_DB) || 0,
}
10. 与其他模块的集成
10.1 健康检查集成
typescript
// 源码位置:apps/server/src/modules/console/health/health.module.ts
@Module({
imports: [
TerminusModule.forRoot({
errorLogStyle: "pretty",
}),
HttpModule,
RedisModule, // 导入Redis模块
DatabaseModule,
],
controllers: [HealthController],
providers: [AppHealthIndicator, DatabaseHealthIndicator, RedisHealthIndicator],
})
export class HealthModule {}
10.2 队列系统集成
typescript
// 源码位置:apps/server/src/core/queue/queue.module.ts
BullModule.forRootAsync({
useFactory: async () => ({
redis: {
host: process.env.REDIS_HOST || "localhost",
port: Number(process.env.REDIS_PORT) || 6379,
password: process.env.REDIS_PASSWORD || "",
db: Number(process.env.REDIS_DB) || 0,
},
defaultJobOptions: {
attempts: 3,
removeOnComplete: true,
removeOnFail: false,
},
}),
})
10.3 微信模块集成
typescript
// 源码位置:apps/server/src/common/modules/wechat/wechat.module.ts
@Module({
imports: [ChannelModule, RedisModule], // 导入Redis模块
providers: [WechatOaService, WxOaConfigService, AuthService],
exports: [WechatOaService],
})
export class WechatModule {}
11. 使用示例
11.1 基本使用示例
typescript
// 在其他服务中使用Redis
@Injectable()
export class SomeService {
constructor(private readonly redisService: RedisService) {}
async cacheData(key: string, data: any, ttl: number = 3600) {
await this.redisService.set(key, JSON.stringify(data), ttl);
}
async getCachedData<T>(key: string): Promise<T | null> {
const data = await this.redisService.get<string>(key);
return data ? JSON.parse(data) : null;
}
async clearCache(key: string) {
await this.redisService.del(key);
}
}
11.2 发布订阅示例
typescript
// 发布消息
async publishMessage(channel: string, message: any) {
await this.redisService.publish(channel, JSON.stringify(message));
}
// 订阅消息
async subscribeToChannel(channel: string) {
await this.redisService.subscribe(channel, (message, channel) => {
const data = JSON.parse(message);
console.log(`收到来自 ${channel} 的消息:`, data);
});
}
11.3 微信access_token缓存示例
typescript
// 源码位置:apps/server/src/common/modules/wechat/services/wechatoa.service.ts
async getgetAccessTokenByRedis() {
const { appId } = await this.wxoaconfigService.getConfig();
// 构建缓存键
const cacheKey = `${this.CACHE_PREFIX}:${appId}`;
// 从Redis缓存获取access_token
const cachedResult = await this.redisService.get<string>(cacheKey);
// 如果缓存中没有access_token,则从微信API获取
if (!cachedResult || !this.wechatOaClient) {
const access_token = await this.getAccessToken();
// 将access_token缓存到Redis,有效期设置为7100秒(微信官方是7200秒,提前100秒过期)
await this.redisService.set(cacheKey, access_token, 7200 - 100);
return access_token;
}
return cachedResult;
}
12. 健康检查实现
12.1 Redis健康检查指示器
typescript
// 源码位置:apps/server/src/modules/console/health/indicators/redis.health.ts
@Injectable()
export class RedisHealthIndicator {
constructor(
private readonly redisService: RedisService,
private readonly healthIndicatorService: HealthIndicatorService,
) {}
async isHealthy(key: string): Promise<HealthIndicatorResult> {
const indicator = this.healthIndicatorService.check(key);
try {
const client = this.redisService.getClient();
const result = await client.ping();
const isHealthy = result === "PONG";
if (isHealthy) {
return indicator.up({ message: "Redis连接正常" });
} else {
return indicator.down({ message: "Redis连接异常" });
}
} catch (error) {
return indicator.down({
message: "Redis连接异常",
error: error.message,
});
}
}
}
13. 最佳实践
13.1 配置管理
- 环境变量: 使用环境变量管理 Redis 连接配置
- 默认值: 为所有配置项提供合理的默认值
- 安全性: 避免在代码中硬编码敏感信息
13.2 连接管理
- 连接池: Redis 客户端自动管理连接池
- 错误处理: 监听连接错误并记录日志
- 优雅关闭 : 实现
OnModuleDestroy确保连接正确关闭
13.3 性能优化
- TTL设置: 为缓存数据设置合适的过期时间
- 批量操作: 使用 pipeline 进行批量操作
- 内存管理: 定期清理过期数据
13.4 错误处理
- 重连机制: Redis 客户端自动重连
- 降级策略: 在 Redis 不可用时提供降级方案
- 监控告警: 集成健康检查和监控系统
14. 技术特点总结
14.1 核心特性
- 工厂模式: 灵活的服务实例创建
- 简化架构: 移除 ConfigService 依赖,直接使用环境变量
- 配置驱动: 环境变量驱动的配置管理
- 事件监听: 完善的连接状态监控
- 类型安全: TypeScript 类型支持
14.2 功能完整性
- 基本操作: GET、SET、DEL 等基础功能
- 高级功能: 发布订阅、自定义命令执行
- 健康检查: 集成到应用健康监控系统
- 缓存管理: 支持 TTL 和批量操作
- 连接管理: 自动连接和优雅关闭
14.3 集成能力
- 模块化: 易于集成到其他模块
- 队列支持: 与 Bull 队列系统集成
- 微信集成: 支持微信 access_token 缓存
- 健康监控: 提供健康检查端点
15. 总结
RedisModule 是 FastbuildAI 后端应用的重要基础设施模块,通过工厂模式和环境变量配置提供了灵活、可靠的 Redis 服务。该模块具有以下优势:
- 设计优雅: 使用工厂模式和环境变量配置,代码结构清晰简洁
- 功能完整: 支持基本操作、发布订阅、健康检查等功能
- 架构简化: 移除 ConfigService 依赖,直接使用环境变量,提高性能和可维护性
- 配置灵活: 环境变量驱动,支持多环境部署
- 集成友好: 易于与其他模块集成,支持多种使用场景
- 监控完善: 提供连接状态监控和健康检查功能
该模块为整个应用提供了稳定可靠的缓存和消息队列基础,是系统架构中的重要组成部分。