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

使用nodemon关闭Express服务器时控制台输出不一致问题

Windows下Nodemon启动Node应用时优雅关闭逻辑未完全执行

问题场景

我正在开发大学课程的Node/Express Web应用,核心文件包括:

  • db.js:创建并导出Sequelize数据库实例
  • app.js:创建并导出Express应用实例
  • entry.js:应用启动入口,实现了服务器和数据库的优雅关闭逻辑

通过npm run devstart启动服务(package.json脚本为SET NODE_ENV=dev && nodemon ./app/bin/entry.js),在Windows终端按下Ctrl+C终止进程时,控制台经常只输出到Server is shutting down.,无法打印出Database connection closed successfully.;但直接用node命令启动服务时,整个优雅关闭流程能正常执行,日志完全符合预期。

核心代码片段

db.js

const { Sequelize } = require('sequelize');
const dbConfig = require('./dbconfig');

// Create Sequelize instance
const NODE_ENV = process.env.NODE_ENV.trim();
const sequelize = new Sequelize(dbConfig[NODE_ENV]);

module.exports = sequelize;

app.js

const express = require("express");
const app = express();
module.exports = app;

entry.js

const sequelize = require('../../db/db');
const app = require('../app');

const port = 3000;

// Test database connection
const testDatabaseConnection = async () => {
    try {
        await sequelize.authenticate();
        console.log('Database connection successfull.');
      } catch (error) {
        console.error('Unable to connect to the database:', error);
        throw(error);
    }
}

// Attempt to start the server
const serverStart = async () => {
    try {
        await testDatabaseConnection();
        const server = app.listen( port, () => {
            console.log(`Server listening on port ${port}.`);
        });
        return server;
    } catch(error) {
        console.error('Failed to start server:', error);
        process.exit(1);
    }
}

serverStart().then( (server) => {
    // Listen for process termination signals
    process.on('SIGINT', () => gracefulShutdown(server));
    process.on('SIGTERM', () => gracefulShutdown(server));
} );

// Gracefully close the application
const gracefulShutdown = async (server) => {
    
    console.log('Server is shutting down.');

    try {
        await new Promise( (resolve, reject) => {
            server.close( (error) => (error ? reject(error) : resolve()) );
        } );
        await sequelize.close();
        console.log('Database connection closed successfully.');

        process.exit(0);

    } catch(error) {
        console.error('Error during shutdown:', error);
        process.exit(1);
    }
};

原因分析

  1. Windows下Nodemon的信号处理时序问题:当按下Ctrl+C时,Windows终端会先向Nodemon进程发送终止信号,Nodemon默认可能不等子进程(你的Node应用)完成异步关闭逻辑,就触发了批处理的Terminate batch job (Y/N)?提示,导致子进程的后续日志无法输出。
  2. 信号重复触发:多次按下Ctrl+C可能导致SIGINT信号被多次传递,打乱异步关闭流程的执行顺序。

解决方案

1. 修改Nodemon启动脚本,添加--exitcrash参数

--exitcrash参数会让Nodemon等待子进程完全退出后再自身终止,避免提前触发批处理终止提示。修改package.json的scripts:

"scripts": {
  "devstart": "SET NODE_ENV=dev && nodemon --exitcrash ./app/bin/entry.js"
}

2. 优化信号监听逻辑,确保异步操作完成

使用process.once代替process.on,避免重复触发关闭逻辑;同时确保异步的gracefulShutdown函数完全执行:

serverStart().then( (server) => {
  // 用once确保关闭逻辑只执行一次
  process.once('SIGINT', async () => {
    await gracefulShutdown(server);
  });
  process.once('SIGTERM', async () => {
    await gracefulShutdown(server);
  });
} );

3. 统一跨平台环境变量设置(可选)

Windows的SET命令和类Unix系统的环境变量设置方式不同,使用cross-env可以统一处理,避免潜在的环境变量传递问题:

  1. 安装依赖:
npm install cross-env --save-dev
  1. 修改启动脚本:
"scripts": {
  "devstart": "cross-env NODE_ENV=dev nodemon --exitcrash ./app/bin/entry.js"
}

4. 验证数据库关闭操作的异步执行

可以显式确保sequelize.close()完成后再打印日志,避免因异步时序问题导致日志未输出:

// 在gracefulShutdown函数中修改
await sequelize.close();
console.log('Database connection closed successfully.');
// 或者添加then回调确认
await sequelize.close().then(() => {
  console.log('Database connection closed successfully.');
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 18:04:51