如何在节点 js 中保持分叉 child 进程存活

How to keep forked child process alive in node js

我想用 node 创建一个 rabbitmq cli 运行ning 就像 foreverjs。它可以生成 child_process 并在后台保持 运行ning 并且可以随时与 child_process 通信。我面临的问题是当主 cli 程序退出时,child_process 似乎也停止了 运行ning,我尝试用 detached:true 和 .unref() 进行分叉,但它不起作用。即使在 parent 调用进程退出后,我如何在后台 运行 一个 child 进程?

cli.js - parent

const { fork, spawn } = require('child_process');
const options = {
  stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
  slient:true,
  detached:true
};

child = fork('./rabbit.js', [], options)

child.on('message', message => {
  console.log('message from child:', message);
  child.send('Hi');
  // exit parent
  process.exit(0);
});

child.unref()

rabbit.js - child 如果启动并且 运行ning,'i' 应该继续递增

var i=0;
i++;
if (process.send) {
  process.send("Hello"+i);
}

process.on('message', message => {
  console.log('message from parent:', message);
});

我认为 fork 没有 detached 选项。参考 node docs for fork.

如果您使用 spawn,即使 parent 退出,child 也会保留 运行。我已经稍微修改了您的代码以使用 spawn.

cli.js

const { fork, spawn } = require('child_process');
const options = {
  slient:true,
  detached:true,
    stdio: [null, null, null, 'ipc']
};

child = spawn('node', ['rabbit.js'], options);
child.on('message', (data) => {
    console.log(data);
    child.unref();
    process.exit(0);
});

rabbit.js

var i=0;
i++;
process.send(i);
// this can be a http server or a connection to rabbitmq queue. Using setInterval for simplicity
setInterval(() => {
    console.log('yash');
}, 1000);

我认为当你使用 fork 时,在 parent 和 child 进程之间建立了一个 IPC channel。您可以尝试在退出 parent 进程之前优雅地断开 IPC channel 的连接。我会尝试一下,如果有效,我会更新答案。

更新:

我已经更新了 cli.jsrabbit.js 以使其按要求工作。诀窍是在 stdio 选项中使用 ipc 文件描述符。这样您就可以从 child 与 parent 进行通信。如果标记为 null,前三个 fd 将是默认值。有关详细信息,请参阅 stdio options docs

一个老问题,但对于那些了解我今天所处位置的人来说:fork 确实有一个 detached 选项。但是,如果您想打破 parent 和 child.

之间的关系,它还会打开一个必须用 disconnect() 显式关闭的 IPC 通道

在我的例子中,在我确认 child 进程已准备好执行其工作之前使用通道是有利的,然后断开它:

        // Run in background
        const handle = cp.fork('./service/app.js', {
          detached: true,
          stdio: 'ignore'
        });
        // Whenever you are ready to stop receiving IPC messages
        // from the child
        handle.unref();
        handle.disconnect(); 

这允许我的 parent 进程退出而不会终止后台进程或通过引用它来保持活动状态。

如果您确实建立了任何 handle.on(...) 处理程序,最好也将它们与 handle.off(...) 断开连接,当您使用完它们时也是如此。我使用 handle.on('message', (data) => { ... }) 处理程序允许 child 在完成一些异步启动工作后通知 parent 它何时准备好履行其职责。

fork 和 spawn 都有 detached 选项。

但是,当parent进程退出时,child可能想要写入初始标准输出(通过“process.stdout.write”、“console.log”等)。 但是,此标准输出可能不再可用(因为 parent 已死),从而在 child 进程中引发一些异常(例如,管道损坏)。这些异常可能会导致 child 也意外失败。

如果我们允许child写入某些始终可用的输出(例如文件),它将不再失败,因为它仍然可以将信息写入有效实体。

/**
 * This code apart from the comments is available on the Node website
 */

// We use fork, but spawn should also work
const {fork} = require('child_process');

let out = fs.openSync("/path/to/outfile", "a");
let err = fs.openSync("/path/to/errfile", "a");

const child = fork(jsScriptPath, ["--some", "arg"], {
    detached: true,
    stdio: ["pipe", out, err, "ipc"], // => Ask the child to redirect its standard output and error messages to some files
    // silent is overriden by stdio
});

// SetTimeout here is only for illustration. You will want to use more valid code
setTimeout( () => {
    child.unref();
    process.exit(0);
}, 1000);