'How to keep forked child process alive in node js

I want to create a rabbitmq cli running like foreverjs with node. It can spawn child_process and keep it running in the background and can communicate with child_process at any time. The problem I am facing is when main cli program exit the child_process seems to stop running as well, I tried to fork with detached:true and .unref() it doesn't work. How do i run a child process in the background even after the parent caller process exited?

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 if it is up and running, 'i' should keep incrementing

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

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


Solution 1:[1]

I think fork doesn't have a detached option. Refer node docs for fork.

If you use spawn, the child keeps running even if the parent exits. I have modified your code a bit to use 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);

I think when you use fork, an IPC channel is established between the parent and the child process. You could try disconnecting the IPC channel gracefully before exiting the parent process. I'll try it out and update the answer if it works.

Update:

I have updated cli.js and rabbit.js to get it working as asked. The trick is to use ipc file descriptor in stdio options. That way you can communicate with the parent from the child. The first three fds will be the default values if marked as null. For more info, refer stdio options docs

Solution 2:[2]

An old question, but for those picking up where I am today: fork does have a detached option. However it also opens an IPC channel which has to be explicitly closed with disconnect() as well if you want to break the relationship between the parent and the child.

In my case it was advantageous to use the channel until I had confirmation that the child process was ready to do its job, and then disconnect it:

        // 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(); 

This allows my parent process to exit without killing the background process or being kept alive by a reference to it.

If you do establish any handle.on(...) handlers, it's a good idea to disconnect them with handle.off(...) as well when you are through with them. I used a handle.on('message', (data) => { ... }) handler to allow the child to tell the parent when it was ready for its duties after doing some async startup work.

Solution 3:[3]

Both fork and spawn have the detached option.

However, when the parent process exits, the child may want to write to the initial standard output (via "process.stdout.write", "console.log", etc.). However, this standard output may no longer be available (as the parent died), raising some exceptions (for instance, broken pipe) in the child process. These exceptions may cause the child also to fail unexpectedly.

If we allow the child to write to some output always available (files, for example), it will no longer fail, as it can still write information to a valid entity.

/**
 * 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);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3