使用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); } };
原因分析
- Windows下Nodemon的信号处理时序问题:当按下Ctrl+C时,Windows终端会先向Nodemon进程发送终止信号,Nodemon默认可能不等子进程(你的Node应用)完成异步关闭逻辑,就触发了批处理的
Terminate batch job (Y/N)?提示,导致子进程的后续日志无法输出。 - 信号重复触发:多次按下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可以统一处理,避免潜在的环境变量传递问题:
- 安装依赖:
npm install cross-env --save-dev
- 修改启动脚本:
"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
相关产品推荐
相关产品推荐

