Node 多进程编程

1,089 阅读2分钟

在多进程编成过程中,需要重点处理好父,子进程之间的关系,否则很容易出现孤儿进程和僵尸进程,造成意想不到的后果。

孤儿 / 僵尸 进程

  • 孤儿进程 - 当父进程先于子进程退出时,子进程由系统进程接管
  • 僵尸进程 - 当子进程退出时,父进程没有处理子进程退出的消息

模拟孤儿进程

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 669073i'm daddy 669066i'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",欢迎留言讨论,我会尽可能回复,感谢您的阅读。