FWeinb / electron-screenshot-service

Take screenshots using electron
MIT License
141 stars 26 forks source link

Clustering and zombie processes #36

Open ThauEx opened 7 years ago

ThauEx commented 7 years ago

Hi there, I'm using this library together with the node cluster module to get a higher performance. I had the issue that after a while no screenshot process was responding, because it was still busyot stuck, that why I'm killing the worker after about one hour. But the screenshot processes which were stuck are not getting killed, they remain as zombie processes. I tried to use ps tree, to kill all children before killing the worker, but the electron processes were started as a child of the master process instead of the child process. How can I prevent the zombie processes or I am closing the electron process not correctly?

This is a simplified version of my entry file.

if (cluster.isMaster) {
  log.info(`Master ${process.pid} is running`);

  // Fork workers.
  for (let i = 0; i < numCPUs; i++) {
    createFork();
  }

  cluster.on('exit', (worker, code, signal) => {
    log.info(`worker ${worker.process.pid} died`);
    const newWorker = createFork();

    log.info(`Worker ${newWorker.process.pid} started`);
  });

  function createFork() {
    let worker = cluster.fork();
    let killTimeout;
    let shutdownTimeout;

    worker.on('listening', (address) => {
      shutdownTimeout = setTimeout(() => {
        log.info('Cleaning up old workers');
        worker.send('shutdown');
        worker.disconnect();
        killTimeout = setTimeout(() => {
          worker.kill();
        }, 90000);
      }, 3600000 + Math.floor((Math.random() * 100000) + 90000));
    });

    worker.on('disconnect', () => {
      clearTimeout(killTimeout);
      clearTimeout(shutdownTimeout);
    });

    return worker;
  }
} else {
  const http = require('http');
  const screenshot = require('electron-screenshot-service');

  const server = http.createServer((req, res) => {
    […] (calling code from different files)
  });
  server.listen(8080, '0.0.0.0', err => {
    log.info('listening on ' + address + ':' + port);

    if (err) {
      log.error(err, err.stack.split('\n'));
      process.exit(1);
    }
  });

  log.info(`Worker ${process.pid} started`);

  process.on('message', (msg) => {
    if (msg === 'shutdown') {
      server.close(() => {
        screenshot.close();
      });
    }
  });
}

On a specific action, the webserver is calling the screenshot command, like this:

const screenshot = require('electron-screenshot-service');
[…]
    screenshot({
      url: url,
      partition: 'in-memory', // Disable electron cache
      width: 1024,
      height: 768,
      page: true,
    .then(function (img) {
      callback(true, img.data);
    })
    .catch(function (err) {
      callback(false, 'Failed to create screenshot');
    });
[…]