Node.js - 如何防止中断的子进程存活?

时间:2015-09-13 07:28:14

标签: javascript node.js ghostscript child-process spawn

我发现如果调用脚本被中断,某些子进程无法终止。

具体来说,我有一个使用Ghostscript执行各种操作的模块:提取页面图像,从切片创建新的pdf等。我使用以下命令执行命令并返回子项的标准输出的直流:< / p>

function spawnStream(command, args, storeStdout, cbSuccess) {
  storeStdout = storeStdout || false;

  const child = spawn(command, args);
  const stream = through(data => stream.emit('data', data));

  let stdout = '';
  child.stdout.on('data', data => {
    if (storeStdout === true) stdout += data;
    stream.write(data);
  });

  let stderr = '';
  child.stderr.on('data', data => stderr += data);

  child.on('close', code => {
    stream.emit('end');
    if (code > 0) return stream.emit('error', stderr);
    if (!!cbSuccess) cbSuccess(stdout);
  });

  return stream;
}

这由以下函数调用:

function extractPage(pathname, page) {
  const internalRes = 96;
  const downScaleFactor = 1;
  return spawnStream(PATH_TO_GS, [
    '-q',
    '-sstdout=%stderr',
    '-dBATCH',
    '-dNOPAUSE',
    '-sDEVICE=pngalpha',
    `-r${internalRes}`,
    `-dDownScaleFactor=${downScaleFactor}`,
    `-dFirstPage=${page}`,
    `-dLastPage=${page}`,
    '-sOutputFile=%stdout',
    pathname
  ]);
}

,例如,这样消耗:

it('given a pdf pathname and page number, returns the image as a stream', () => {
  const document = path.resolve(__dirname, 'samples', 'document.pdf');
  const test = new Promise((resolve, reject) => {
    const imageBlob = extract(document, 1);
    imageBlob.on('data', data => {
      // do nothing in this test
    });

    imageBlob.on('end', () => resolve(true));
    imageBlob.on('error', err => reject(err));
  });

  return Promise.all([expect(test).to.eventually.equal(true)]);
});

当这被中断时,例如,如果测试超时或发生未处理的错误,则子进程似乎没有收到任何信号并幸存。这有点令人困惑,因为没有任何单独的操作特别复杂,但这个过程似乎无限期地存在,使用100%的CPU。

☁  ~  ps aux | grep gs | head -n 5
rwick            5735 100.0  4.2  3162908 699484 s000  R    12:54AM   6:28.13 gs -q -sstdout=%stderr -dBATCH -dNOPAUSE -sDEVICE=pngalpha -r96 -dDownScaleFactor=1 -dFirstPage=3 -dLastPage=3 -sOutputFile=%stdout /Users/rwick/projects/xan-desk/test/samples/document.pdf
rwick            5734 100.0  4.2  3171100 706260 s000  R    12:54AM   6:28.24 gs -q -sstdout=%stderr -dBATCH -dNOPAUSE -sDEVICE=pngalpha -r96 -dDownScaleFactor=1 -dFirstPage=2 -dLastPage=2 -sOutputFile=%stdout /Users/rwick/projects/xan-desk/test/samples/document.pdf
rwick            5733 100.0  4.1  3154808 689000 s000  R    12:54AM   6:28.36 gs -q -sstdout=%stderr -dBATCH -dNOPAUSE -sDEVICE=pngalpha -r96 -dDownScaleFactor=1 -dFirstPage=1 -dLastPage=1 -sOutputFile=%stdout /Users/rwick/projects/xan-desk/test/samples/document.pdf
rwick            5732 100.0  4.2  3157360 696556 s000  R    12:54AM   6:28.29 gs -q -sstdout=%stderr -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=%stdout /Users/rwick/projects/xan-desk/test/samples/document.pdf /Users/rwick/projects/xan-desk/test/samples/page.pdf

我想用一个计时器向孩子发送一个杀戮信号,但是选择一个任意间隔来杀死一个进程似乎有效地为一个未知的问题交换一个已知问题并且可以将其踢到路上。

我真的很感激任何洞察我在这里缺少的东西。有没有更好的选项来封装子进程,所以父进程的终止更可能导致孩子的中断?

1 个答案:

答案 0 :(得分:1)

听错误事件

child.on('error', function(err) {
    console.error(err);
    // code
    try {
        // child.kill() or child.disconnect()
    } catch (e) {
        console.error(e);
    }
});