NestJS - 即使 Redis 未运行也如何启动应用程序

问题描述 投票:0回答:1

如果 Redis 已启动并正在运行,并且我启动了 Nestjs 应用程序,则它会按预期工作。如果在我的 Nestjs 应用程序运行时 Redis 出现故障,没问题,我有事件侦听器,它将继续尝试重新连接等。但是,如果在我尝试启动 NestJS 应用程序之前 Redis 出现故障,则该应用程序不会启动。我到处都找过了,但找不到基本上忽略该错误的方法。即使无法建立 Redis 连接,我也希望我的应用程序能够继续启动。这是相关的代码片段。

package.json

{
  "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/config": "^3.2.0",
    "@nestjs/core": "^10.0.0",
    "@nestjs/platform-express": "^10.0.0",
    "@nestjs/cache-manager": "^2.2.1",
    "@nestjs/schedule": "^4.0.1",
    "cache-manager": "^5.4.0",
    "cache-manager-redis-store": "^3.0.1",
    "reflect-metadata": "^0.2.0",
    "rxjs": "^7.8.1"
  }
}

main.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    abortOnError: false,
  });

  await app.listen(3000);
}

bootstrap();

app.module.ts

import { Module } from '@nestjs/common';
import { CacheModule } from '@nestjs/cache-manager';
import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { RedisClientEventListener, RedisOptions } from './configs/redis.config';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    CacheModule.registerAsync(RedisOptions),
    ScheduleModule.forRoot(),
  ],
  controllers: [...],
  providers: [...],
})
export class AppModule extends RedisClientEventListener {}

redis.config.ts

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';

export const RedisOptions: CacheModuleAsyncOptions = {
  isGlobal: true,
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => {
    const store = await redisStore({
      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 - Max Connection Retries - Exiting')
          }
          return 3000;
        },
        tls: process.env.NODE_ENV !== 'local',
        rejectUnauthorized: false,
      },
    });
    return {
      store: () => store,
    };
  },
  inject: [ConfigService],
};

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("starting AppModule...")
    const client = this.redisStore.getClient();

    if (!client) {
      this.logger.error('no redis client initialised');
      CacheService.isHealthy = false;
    }

    if (client.isReady) {
      this.logger.log('redis client is connected and ready'); // initial connection to redis upon node app starting, never triggered again
      CacheService.isHealthy = true;
    }

    client.on('connect', () => {
      this.logger.log('redis client is connecting to server...');
    });

    client.on('ready', () => {
      this.logger.log('redis client is ready'); // after re-connecting to redis successfully this gets triggered
      CacheService.isHealthy = true;
    });

    client.on('end', () => {
      this.logger.log('redis client connection closed');
      CacheService.isHealthy = false;
    });

    client.on('error', (error: Error) => {
      this.logger.error(error, 'redis client received an error'); // redis server dies/connection stopped this gets triggered
      CacheService.isHealthy = false;
    });
  }

  async onModuleDestroy(): Promise<void> {
    await this.redisStore.getClient().quit();
  }
}

我运行 npm run start 并关闭 Redis 的结果

[Nest] 7572  - 02/28/2024, 9:41:26 AM     LOG [NestFactory] Starting Nest application...
[Nest] 7572  - 02/28/2024, 9:41:26 AM     LOG [InstanceLoader] ConfigHostModule dependencies initialized +10ms
[Nest] 7572  - 02/28/2024, 9:41:26 AM     LOG [InstanceLoader] DiscoveryModule dependencies initialized +0ms
[Nest] 7572  - 02/28/2024, 9:41:26 AM     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 7572  - 02/28/2024, 9:41:26 AM     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 7572  - 02/28/2024, 9:41:26 AM     LOG [InstanceLoader] ScheduleModule dependencies initialized +6ms
[Nest] 7572  - 02/28/2024, 9:41:26 AM   ERROR [ExceptionHandler] connect ECONNREFUSED ::1:6379
Error: connect ECONNREFUSED ::1:6379
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16)
Error: connect ECONNREFUSED ::1:6379
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16)
redis nestjs
1个回答
0
投票

我认为这个打破了DI(依赖注入)模式,DI需要在使用之前实例化类,否则你不应该像DI那样使用。

© www.soinside.com 2019 - 2024. All rights reserved.