节点子进程:如何拦截类似SIGINT的信号 [英] Node child processes: how to intercept signals like SIGINT

查看:25
本文介绍了节点子进程:如何拦截类似SIGINT的信号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的Node应用程序中,我挂住了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");
});

推荐答案

默认情况下,child_process.spawn()创建的子进程与父进程具有相同的process group,除非它们是使用{detached:true}option调用的。

结果是此脚本在不同环境中的行为不同:

// 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');
});

在交互式shell上,默认情况下会将来自CTL-C的SIGINT发送到整个组,因此未分离的子级将获得SIGINT并退出:

you@bash $ node spawn-test.js
^Cjust ignore SIGINT
# the parent process continues here, let's check children in another window:
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 on parent

.但是调用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

然而,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

这似乎是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

这篇关于节点子进程:如何拦截类似SIGINT的信号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆