如何让NestJS应用在Redis未运行时仍能正常启动
实现NestJS在Redis未启动时正常启动的方案
问题现状
- Redis正常运行时,NestJS应用启动正常;
- 应用运行中Redis宕机,已配置的事件监听可自动重试重连;
- 启动NestJS前Redis已宕机,应用会因Redis连接失败直接启动失败,报错
connect ECONNREFUSED。
问题根源
在RedisOptions的useFactory中直接await redisStore()创建Redis存储时,若Redis不可达,该调用会直接抛出错误,导致CacheModule初始化失败,进而阻断整个NestJS应用的启动流程。
解决方案
核心思路是:将Redis客户端的初始化与应用启动流程解耦,避免连接失败直接阻断模块初始化,同时保留自动重连逻辑。具体修改如下:
1. 修改Redis配置,手动控制客户端连接时机
修改redis.config.ts中的RedisOptions,先创建Redis客户端并关闭自动连接,再手动触发连接(连接失败不阻断存储创建),最后将客户端传入redisStore:
import { CACHE_MANAGER, CacheModuleAsyncOptions } from '@nestjs/cache-manager'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { RedisStore, redisStore } from 'cache-manager-redis-store'; import { Inject, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; import { Cache } from 'cache-manager'; import { CacheService } from '../services/cache.service'; import { createClient } from '@redis/client'; export const RedisOptions: CacheModuleAsyncOptions = { isGlobal: true, imports: [ConfigModule], useFactory: async (configService: ConfigService) => { // 创建Redis客户端,关闭自动连接 const client = createClient({ socket: { host: configService.get<string>('API_REDIS_HOST'), port: parseInt(configService.get<string>('API_REDIS_PORT')!), connectTimeout: 3000, reconnectStrategy(retries: number): number | Error { if (retries > 39) { console.log('Redis - 达到最大重连次数'); return new Error('Max retries reached'); } return 3000; }, tls: process.env.NODE_ENV !== 'local', rejectUnauthorized: false, }, autoConnect: false, // 禁用自动连接,避免初始化时抛出错误 }); // 手动触发连接,连接失败仅打印日志,不阻断流程 client.connect().catch(err => { console.error('Redis初始连接失败,将自动重试:', err); }); // 使用已创建的客户端创建redisStore const store = await redisStore({ client: client, }); return { store: () => store, }; }, inject: [ConfigService], };
2. 优化Redis事件监听逻辑
调整RedisClientEventListener,适配客户端延迟连接的场景,确保事件监听正常生效:
export class RedisClientEventListener implements OnModuleInit, OnModuleDestroy { private readonly logger = new Logger(RedisClientEventListener.name); private readonly redisStore: RedisStore; constructor(@Inject(CACHE_MANAGER) readonly cacheManager: Cache) { this.redisStore = cacheManager.store as unknown as RedisStore; } onModuleInit(): void { console.log("AppModule启动中...") const client = this.redisStore.getClient(); if (!client) { this.logger.error('Redis客户端未初始化'); CacheService.isHealthy = false; return; } // 初始状态判断 CacheService.isHealthy = client.isReady; if (client.isReady) { this.logger.log('Redis客户端已连接就绪'); } else { this.logger.log('Redis客户端待连接,将自动重试'); } // 绑定各类事件监听 client.on('connect', () => { this.logger.log('Redis客户端正在连接服务器...'); }); client.on('ready', () => { this.logger.log('Redis客户端连接就绪'); CacheService.isHealthy = true; }); client.on('end', () => { this.logger.log('Redis客户端连接已关闭'); CacheService.isHealthy = false; }); client.on('error', (error: Error) => { this.logger.error(error, 'Redis客户端发生错误'); CacheService.isHealthy = false; }); } async onModuleDestroy(): Promise<void> { const client = this.redisStore.getClient(); // 根据客户端状态选择关闭方式 if (client.isReady) { await client.quit(); } else { await client.disconnect(); } } }
3. 保留应用启动错误容忍配置
确保main.ts中的abortOnError: false配置生效,避免其他非致命错误阻断启动:
async function bootstrap() { const app = await NestFactory.create(AppModule, { abortOnError: false, }); await app.listen(3000); } bootstrap();
验证效果
启动NestJS前关闭Redis,此时应用会正常启动,控制台打印Redis初始连接失败,将自动重试,之后Redis客户端会按照配置的策略自动重试连接。当Redis启动后,客户端会触发ready事件,CacheService.isHealthy会自动切换为true,应用可正常使用Redis缓存。
内容的提问来源于stack exchange,提问作者user1830833
相关产品推荐
相关产品推荐

