在多进程编成过程中,需要重点处理好父,子进程之间的关系,否则很容易出现孤儿进程和僵尸进程,造成意想不到的后果。
孤儿 / 僵尸 进程
- 孤儿进程 - 当父进程先于子进程退出时,子进程由系统进程接管
- 僵尸进程 - 当子进程退出时,父进程没有处理子进程退出的消息
模拟孤儿进程
const cp = require('child_process');
if (!process.send) { // 主进程
const child = cp.fork(__filename);
child.disconnect(); // 关闭 ipc 通道
child.unref(); // 必须关闭 ipc 通道后, 主进程才会退出
console.log(`bye my son ${child.pid}, i'm daddy ${process.pid}`);
} else { // 子进程
setInterval(() => {
console.log(`OMG, i'm alive, where is my daddy ${process.ppid}`)
}, 2000)
}
结果输出: 父进程退出, 子进程保留
$ node test.js
bye my son 665666, i'm daddy 665659
$ OMG, i'm alive, where is my daddy 1678
OMG, i'm alive, where is my daddy 1678
OMG, i'm alive, where is my daddy 1678
子进程接管: 子进程被 systmd 接管
$ ps -L 1678
PID LWP TTY STAT TIME COMMAND
1678 1678 ? Ss 0:04 /lib/systemd/systemd --user
模拟僵尸进程
const cp = require('child_process');
if (!process.send) {
const child = cp.fork(__filename);
console.log(`sorry my son ${child.pid}, i'm daddy ${process.pid}, i'm too busy too look after you`);
while(true) {}; // 保持忙碌
} else {
console.log(`daddy !!!??? i'm ${process.pid} hungry to die`);
}
结果输出: 子进程退出,父进程没及时处理
$ node test.js
sorry my son 669073, i'm daddy 669066, i'm too busy too look after you
daddy !!!??? i'm 669073 hungry to die
子进程转换为僵尸: Zombie
$ ps -L 669073
PID LWP TTY STAT TIME COMMAND
669073 669073 pts/3 Z+ 0:00 [node] <defunct>
常见问题
IPC 通道关闭
在调用 kill, disconnect 后,父子进程之间的 IPC 通道会立刻关闭,通道关闭后 process.connected === false
或者 process.channel === undefined
, 此时如果再继续通过 IPC 通信则会抛出错误。
safeSend
function safeSend(args) {
process.channel && process.send(args)
}
注: disconnet 只会关闭 IPC, 不会关闭子进程
优雅退出
不要使用强制退出信号 SIGINT,否则会造成孤儿进程, 推荐使用 SIGTERM, 因为 SIGTERM 可以被阻断,从而有时间退出前的清理工作。
clean up
process.on('SIGTERM', () => cleanup())
process.on('SIGINT', () => {
console.warn('suggest not to use CTRL+C, this operation will produce orphan process');
cleanup();
})
function cleanup() {
child.kill(); // 默认使用 SIGTERM 信号
process.exit(0);
}
子进程集群管理
可以参考 Cluster 模块
总结
先入为主大杀手 虽然知道 Node 的 Cluster 模块, 但是由于之前先入为主的认识: Cluster 模块通常用在请求的负载均衡使用场景中, 所以没有往这个核心模块上考虑, 当时还在考虑自己写一个模块🤣。今天读了下Cluster 源码, 只能说👍弯的four(wonderful)。Cluster 不能取代 fork, 在可以 Cluster 场景下,推荐它;在只能使用 fork 场景下,参考它。
关注我的微信公众号"SUNTOPO WLOG",欢迎留言讨论,我会尽可能回复,感谢您的阅读。