You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优雅关闭NestJS应用 触发unhandledRejection时正常终止进程

问题原因

你当前代码不生效的核心原因有两个:

  1. unhandledRejection 事件监听器注册时机太晚,放在了 await app.listen() 之后,应用启动过程中触发的未处理Promise rejection无法被捕获;如果是运行阶段的rejection触发后进程没退出,大概率是app.close()执行时被未释放的资源卡住,导致后续的process.exit(1)始终无法执行。
  2. 异步的事件回调没有做异常兜底,一旦回调内部触发新的错误,会导致逻辑中断。
修复方案

修改后的完整代码如下:

import { INestApplication } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
// 其余业务导入保持不变

/**
 * @desc - 启动应用,所有模块的组装入口
 */
async function bootstrap() {
  let app: INestApplication;
  // 无依赖注入的日志实例
  const logger = new LogService();

  process.on('uncaughtException', (err: Error) => {
    logger.log('❌❌❌ ➡ ⬇⬇⬇ 发生未捕获异常错误 ⬇⬇⬇');

    const error = {
      name: err.name,
      message: err.message,
      stack: err.stack,
    };

    // 控制台打印错误
    logger.error(err.message, error.stack);
    // 写入错误日志文件
    logger.exception(error);

    setTimeout(() => {
      process.exit(1);
    }, 100);
  });

  process.on('unhandledRejection', async (err: Error) => {
    logger.log('❌❌❌ ➡ ⬇⬇⬇ 发生未处理的Promise rejection错误 ⬇⬇⬇');
    try {
      const error = {
        name: err.name,
        message: err.message,
        stack: err.stack,
      };

      // 控制台打印错误
      logger.error(error.message, error.stack);
      // 写入拒绝日志文件
      logger.rejection(error);

      // 仅当应用实例已创建成功时关闭应用
      if (app) {
        // 增加超时兜底,3秒未完成关闭则强制退出
        const closeTimeout = setTimeout(() => process.exit(1), 3000);
        await app.close();
        clearTimeout(closeTimeout);
      }
    } catch (e) {
      logger.error('优雅关闭过程出错,强制退出', e.stack);
    } finally {
      process.exit(1);
    }
  });

  try {
    // 创建Nest应用实例
    app = await NestFactory.create(AppModule, {
      logger,
    });

    // 开启关闭钩子监听
    app.enableShutdownHooks();

    // 获取配置服务读取环境变量
    const config_service = app.get(ConfigService);
    const HOST: string = config_service.get<string>(CONFIG.HOST);
    const PORT: number = config_service.get<number>(CONFIG.PORT);

    // 全局参数校验管道
    app.useGlobalPipes(new ValidationPipe(VALIDATION_RULE));
    // 全局异常过滤器
    app.useGlobalFilters(new HttpExceptionFilter());

    await app.listen(PORT);
    logger.log(`ℹℹℹ 服务已启动,监听地址 ${HOST}:${PORT} ℹℹℹ`);
  } catch (startErr) {
    logger.error('应用启动失败', startErr.stack);
    process.exit(1);
  }
}

bootstrap();
说明
  • 方案同时兼容启动阶段和运行阶段的未处理rejection场景,启动阶段触发异常会直接退出,运行阶段会先尝试优雅关闭应用、释放端口和资源后再退出
  • 如果开启了数据库、Redis等长连接,确保对应模块的onModuleDestroy生命周期钩子中已经实现了连接销毁逻辑,否则app.close()可能会被未释放的长连接卡住

内容的提问来源于stack exchange,提问作者Peter Joshua

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 20:39:04