【问题标题】:node.js - reading child process stdout 100 bytes at a timenode.js - 一次读取子进程标准输出 100 个字节
【发布时间】:2016-04-30 12:34:09
【问题描述】:

我正在生成一个产生大量数据的孩子(我在这里使用“ls -lR /”作为示例)。我想一次异步读取孩子的标准输出 100 个字节。

所以我想做:get100().then(process100).then(get100).then(process100).then(...

由于某种原因,这段代码只循环了 3 次,并且我停止接收 Readable 事件。我想不通为什么?

var Promise = require('bluebird');
var spawn   = require("child_process").spawn;

var exec = spawn( "ls", [ "-lR", "/"] );  

var get100 = function () {
     return new Promise(function(resolve, reject) {
       var tryTransfer = function() {
          var block = exec.stdout.read(100);
          if (block) {
            console.log("Got 100 Bytes");
            exec.stdout.removeAllListeners('readable');  
            resolve();
          } else console.log("Read Failed - not enough bytes?");
        };
        exec.stdout.on('readable', tryTransfer);
    });
};

var forEver = Promise.method(function(action) {
    return action().then(forEver.bind(null, action));
});

forEver(
    function() { return get100(); }
)

【问题讨论】:

  • 这似乎是一种非常复杂的方式来做一个简单的ls。想做什么?读取的 100 个字节如何处理?
  • 我并没有真正运行 ls。我正在运行一些生成大量数据的专有 .exe。我有一份消费者名单。我需要获取前 100 个字节,添加一个标头,然后将其发送给第一个消费者,接下来的 100 + 标头发送给第二个消费者,等等。我正在尝试异步执行此操作。
  • 我明白了,因为 exec.stdout 是一个流,你可以坚持使用流。我正在写一个anwser。
  • 我想我至少找到了部分问题。我认为“可读”事件是针对已到达缓冲区的一些字节发送的。在我们读取此字节数之前,即使有更多字节到达,我们也不会收到另一个事件。
  • 根据文档,readable 在数据块可用时触发,无论其大小如何。我认为问题可能是输入流的读取速度太快,并且有时 100 个字节不可用,因此您的处理就停止了。 .exe 的输出有多大/巨大?将其存储在文件中并处理该文件可能是一种解决方案,您将拥有更多控制权。就像我在下面所说的那样,流式传输/处理一个巨大的 2GB csv 文件对于 nodejs 来说是没有问题的。

标签: node.js stream child-process


【解决方案1】:

使用event-stream,只要有数据要读取(流是异步的),您就可以发出 100 字节数据从衍生进程中读取(流是异步的):

var es = require('event-stream');
var spawn = require("child_process").spawn;

var exec = spawn("ls", ["-lR", "/"]);

var stream = es.readable(function (count, next) {
    // read 100 bytes
    while (block = exec.stdout.read(100)) {
        // if you have tons of data, it's not a good idea to log here
        // console.log("Got 100 Bytes");
        // emit the block
        this.emit('data', block.toString()); // block is a buffer (bytes array), you may need toString() or not
    }

    // no more data left to read
    this.emit('end');
    next();
}).on('data', function(data) {
    // data is the 100 bytes block, do what you want here

    // the stream is pausable and resumable at will
    stream.pause();
    doStuff(data, function() {
        stream.resume();
    });
});

【讨论】:

  • 如果我理解正确,该流将被尽快读取并转换为对 doStuff(data) 的调用。那么如果doStuff跟不上,进程最终不会耗尽内存吗?
  • 尝试按原样运行此代码。 doStuff 从未被调用过?
  • @GroovyDotCom,是的,它会尽可能快地流式传输,但如果需要,您可以暂停输入流(我正在编辑答案以添加示例)。 doStuff() 从未打过电话?这很奇怪,我尝试了一个简单的 console.log,我得到了来自 ls 的长输出,分成 100 个字节:tonicdev.com/shanshan/streamed-stdout
  • tonicdev 上的结果是否完整运行?如果是这样,那就有问题了,因为你的 ls -lR / only 34 * 3 * 100 bytes = 10,000
  • 我从 ls 切换到: var exec = spawn("hexdump", ["/dev/urandom"]);并一次读取 1 个字节。它在停止之前读取 65536 个字节。这个数字不是巧合!出事了。
猜你喜欢
  • 1970-01-01
  • 2011-02-17
  • 2015-01-20
  • 1970-01-01
  • 2011-07-26
  • 1970-01-01
相关资源
最近更新 更多