父进程触发SIGINT时,如何通过forever-monitor通知子进程?
解决方案:通过IPC通道实现父子进程优雅关闭
在Windows环境下,forever-monitor创建的子进程默认处于detached状态,常规的信号传递(比如SIGINT)可能无法直接触发子进程的优雅关闭逻辑。我们可以通过Node.js的IPC进程通信通道来实现父进程向子进程发送关闭指令,具体修改如下:
修改monitor.js(父进程)
我们需要开启IPC通道,并在收到退出信号时向子进程发送关闭指令,同时等待子进程完成关闭(或超时强制关闭):
const path = require('path'); const forever = require('forever-monitor'); let child; function exit() { console.error('Received exit signal on main process.'); if (!child) { process.exit(0); return; } // 向子进程发送优雅关闭指令 child.send('shutdown'); // 设置超时,防止子进程卡住无法退出 const shutdownTimeout = setTimeout(() => { console.log('Child process did not shut down in time, force stopping...'); child.stop(); process.exit(0); }, 5000); // 5秒超时阈值可根据你的服务调整 // 监听子进程退出事件,清除超时并正常退出父进程 child.once('exit', () => { clearTimeout(shutdownTimeout); console.log('Child process exited, shutting down main process.'); process.exit(0); }); } // Windows下SIGKILL无法被捕获,因此移除该监听 ['SIGINT', 'SIGTERM'].forEach(signal => process.on(signal, exit)); // 创建Monitor实例时开启IPC通道 child = new forever.Monitor(path.join(__dirname, 'server.js'), { stdio: ['inherit', 'inherit', 'inherit', 'ipc'] // 前三个参数继承父进程的IO,第四个开启IPC }); child.start();
修改server.js(子进程)
我们需要监听来自父进程的IPC消息,收到关闭指令时调用stopServer函数,同时处理自身的信号监听(确保子进程单独运行时也能优雅关闭):
const express = require('express'); const app = express(); const port = process.env.PORT || 8080; let server; function stopServer(callback) { if (!server) { callback?.(null); process.exit(0); return; } console.log('Shutting down server gracefully...'); server.close((err) => { if (err) { console.error('Failed to shut down server:', err); callback?.(err); process.exit(1); } else { console.log('Server shut down successfully.'); // 如果是子进程,通知父进程关闭完成 if (process.send) { process.send('shutdown-complete'); } callback?.(null); process.exit(0); } }); } // 监听父进程发送的IPC消息 process.on('message', (msg) => { if (msg === 'shutdown') { stopServer(); } }); // 监听子进程自身的退出信号(比如单独运行时的Ctrl+C) ['SIGINT', 'SIGTERM'].forEach(signal => { process.on(signal, () => { stopServer(); }); }); app.get('/', (request, response) => response.send('Hello')); server = app.listen(port, () => console.log(`Server is running on port "${port}".`));
关键说明:
- IPC通道的必要性:在Windows的detached子进程场景下,信号传递机制有限,IPC是跨平台且可靠的进程通信方式,能确保父进程的关闭指令准确传递到子进程。
- 超时保护:添加超时逻辑是为了避免子进程因某些异常无法关闭,导致父进程一直挂起。
- 双向兼容:给子进程也添加自身的信号监听,确保无论子进程是被forever-monitor管理还是单独运行,都能优雅关闭。
内容的提问来源于stack exchange,提问作者Benny Neugebauer
相关产品推荐
相关产品推荐

