child_process 在mac上的输出截断问题

725 阅读1分钟

背景

因为业务的选项,最近要在代码中使用child_process 进行一些计算 并通过stdout的形式返回一些计算结果。 但是在本地的mac环境上开发时,发现了一个奇怪的现象,输出的数据被截断了

逻辑代码

主要的简化代码如下

  • 子进程进行一些计算
  • 输出json数据
  • 退出进程
  • master接受子进程传入的json格式的字符串

master.js

const { spawn } = require('child_process');

const path = require('path');
const { writeFileSync } = require('fs');

const childFilePath = path.resolve(__dirname, './child.js');

const checker = spawn('node', [childFilePath]);

new Promise((resolve, reject) => {
  const resultBuf = [];

  checker.stdout.on('data', (data) => {
    resultBuf.push(data);
  });

  checker.stderr.on('data', (data) => {
    reject(new Error(`stderr: ${data}`));
  });

  checker.on('close', () => {
    const strBuffer = Buffer.concat(resultBuf);
    // buffer 转成 字符串
    const result = strBuffer.toString();
    
    resolve(result);
  });
})
  .then((res) => {
    writeFileSync(path.resolve(__dirname, './output.json'), res);
  });


child.js

// 子进程
const process = require('process');

const result = [];
for (let i = 0; i < 10000; i += 1) {
  result.push(i);
}

process.stdout.write(JSON.stringify(result));

process.exit(0);


问题现场

到了1860后面的就直接截断了

添加一个try catch 看一下截断的字符串的最大长度

    try {
      // 这里做特定的解析 如果被截断,就不是标准的json了
      JSON.parse(result);
    } catch (e) {
      // 输出buffer的长度
      console.log(strBuffer.byteLength);
    }
    resolve(result);

strBuffer的长度是8192 (这个整数太标准了)

问题就出在了子进程的 process.stdout.write和 process.exit之间

现象是 buffer作为运载货车,只拉了一趟货 子进程就退出了,剩下的货还都留在原地

对应的node.js 的issue 有 github.com/nodejs/node…

官方的文档也有特定的说明 nodejs.org/api/process…

解决办法

  1. 在process的输出前添加
process.stdout._handle.setBlocking(true);

process.stdout.write(JSON.stringify(result));

  1. 去掉 process.exit(0);