jul*_*n_c 10 signals child-process node.js
在我的节点应用程序中,我正在挂钩SIGINT信号以便优雅地停止(使用pm2,但这与此无关).
我的应用程序还执行/生成几个子进程.
我可以挂钩SIGINT拦截它并执行优雅停止,但是我的子进程通过相同的信号传递,因此立即被杀死.
如何拦截SIGINT子进程上的信号?
我正在做的一个样本:
const child = child_process.spawn('sleep', ['10000000']);
console.log(`Child pid: ${child.pid}`);
child.on('exit', (code, signal) => { console.log('Exit', code, signal); });
process.on('SIGINT', () => {
console.log("Intercepting SIGINT");
});
Run Code Online (Sandbox Code Playgroud)
小智 8
默认情况下,创建的子进程与父进程child_process.spawn()具有相同的进程组,除非使用该{detached:true} 选项调用它们.
结果是这个脚本在不同的环境中表现不同:
// spawn-test.js
const { spawn } = require('child_process');
const one = spawn('sleep', ['101']);
const two = spawn('sleep', ['102'], {detached: true});
two.unref();
process.on('SIGINT', function () {
console.log('just ignore SIGINT');
});
Run Code Online (Sandbox Code Playgroud)
在交互式shell上,默认情况下来自Ctl-C的SIGINT被发送到整个组,因此非分离的子进程将获得SIGINT并退出:
you@bash $ node spawn-test.js
^Cjust ignore SIGINT
you@bash [another-terminal-window] $ ps aux | grep sleep
... sleep 102
# note that sleep 101 is not running anymore
# because it recieved the SIGINT from the Ctl-C
Run Code Online (Sandbox Code Playgroud)
...但是来电kill(2)可以发出你的父母过程的信号,让孩子们活着:
you@bash $ node spawn-test.js & echo $?
[2] 1234
you@bash [another-terminal-window] $ kill -SIGINT 1234
you@bash [another-terminal-window] $ ps aux | grep sleep
... sleep 101
... sleep 102
# both are still running
Run Code Online (Sandbox Code Playgroud)
然而,pm2是另一头野兽.即使你尝试了上述技术,它也会杀死整个进程树,包括你的分离进程,即使是很长的--kill-timeout:
# Test pm2 stop
you@bash $ pm2 start spawn-test.js --kill-timeout 3600
you@bash $ pm2 stop spawn-test
you@bash $ ps aux | grep sleep
# both are dead
# Test pm3 reload
you@bash $ pm2 start spawn-test.js --kill-timeout 3600
you@bash $ pm2 reload spawn-test
you@bash $ ps aux | grep sleep
# both have different PIDs and were therefore killed and restarted
Run Code Online (Sandbox Code Playgroud)
这似乎是pm2中的一个错误.
我通过使用init系统(在我的情况下是systemd)而不是pm2来解决类似的问题,因为这样可以更好地控制信号处理.
在systemd上,默认情况下会将信号发送到整个组,但您可以使用KillMode=mixed此信号仅将信号发送到父进程,但如果超过超时,则仍然会发送SIGKILL子进程.
我的systemd单元文件如下所示:
[Unit]
Description=node server with long-running children example
[Service]
Type=simple
Restart=always
RestartSec=30
TimeoutStopSec=3600
KillMode=mixed
ExecStart=/usr/local/bin/node /path/to/your/server.js
[Install]
WantedBy=multi-user.target
Run Code Online (Sandbox Code Playgroud)
通常在 C 中,您可以通过忽略子进程中的信号(或者在新进程组中生成它,以便前台进程组的终端生成的信号不会到达它)来解决此问题。
从https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options来看,NodeJs 似乎没有为此公开 API,但是,它确实有一个通过 shell 生成子进程的选项,所以呢?您可以做的就是打开它并忽略 shell 中的信号,这将导致其忽略状态继承给 shell 的子级。
const child_process = require('child_process')
//const child = child_process.spawn('sleep', ['10000000']);
const child = child_process.spawn("trap '' INT; sleep 10000000", [], {shell: true });
console.log(`Child pid: ${child.pid}`);
child.on('exit', (code, signal) => { console.log('Exit', code, signal); });
process.on('SIGINT', () => {
console.log("Intercepting SIGINT");
});
//emulate cat to keep the process alive
process.stdin.pipe(process.stdout);
Run Code Online (Sandbox Code Playgroud)
现在,当您按 Ctrl-C 时,Node 进程会处理它,并且睡眠进程会继续存在。(如果您不熟悉其他终端生成的信号,如果您不介意核心转储,则可以通过按 Ctrl-\(向该组发送 SIGQUIT)轻松终止该组)。
| 归档时间: |
|
| 查看次数: |
3033 次 |
| 最近记录: |