取消管道streams2管道并清空它的正确方法(不仅仅是刷新)

时间:2014-11-06 02:27:19

标签: node.js pipe pipeline node.js-stream

前提

我试图找到在Node.js中过早终止一系列管道流(管道)的正确方法:有时我想在流完成之前优雅地中止流。具体来说,我处理的主要是objectMode: true和非本地并行流,但这并不重要。

问题

问题是当我unpipe管道时,数据保留在每个流的缓冲区中并且drain被编辑。对于大多数中间流(例如Readable / Transform),此可能没问题,但最后Writable仍然会耗尽其写入目标(例如文件)或数据库或套接字或w / e)。如果缓冲区包含数百或数千个块需要花费大量时间来消耗,则这可能是有问题的。我想让它立即停止,即不要流失;为什么浪费周期和记忆数据并不重要?

根据我走的路线,我会收到一个"写完后#34;当流无法找到现有管道时出现错误或异常。

问题

正确的方式a.pipe(b).pipe(c).pipe(z)的形式优雅地删除流的管道是什么?

解?

我提出的解决方案是三步:

  1. unpipe管道中的每个流的顺序相反
  2. 清空实现Writable
  3. 的每个流的缓冲区
  4. end每个实现Writable
  5. 的流

    一些说明整个过程的伪代码:

    var pipeline = [ // define the pipeline
      readStream,
      transformStream0,
      transformStream1,
      writeStream
    ];
    
    // build and start the pipeline
    var tmpBuildStream;
    pipeline.forEach(function(stream) {
        if ( !tmpBuildStream ) {
            tmpBuildStream = stream;
            continue;
        }
        tmpBuildStream = lastStream.pipe(stream);
    });
    
    // sleep, timeout, event, etc...
    
    // tear down the pipeline
    var tmpTearStream;
    pipeline.slice(0).reverse().forEach(function(stream) {
        if ( !tmpTearStream ) {
            tmpTearStream = stream;
            continue;
        }
        tmpTearStream = stream.unpipe(tmpTearStream);
    });
    
    // empty and end the pipeline
    pipeline.forEach(function(stream) {
      if ( typeof stream._writableState === 'object' ) { // empty
        stream._writableState.length -= stream._writableState.buffer.length;
        stream._writableState.buffer = [];
      }
      if ( typeof stream.end === 'function' ) { // kill
        stream.end();
      }
    });
    

    我真的很担心stream._writableState的使用并修改内部bufferlength属性(_表示私有财产)。这似乎是一个黑客。另请注意,由于我正在管道,pauseresume这样的问题我们无从谈起(基于我从IRC收到的建议)。

    我还整理了一个可以从github获取的可运行版本(非常草率):https://github.com/zamnuts/multipipe-proto(git clone,npm install,view readme,npm start)

1 个答案:

答案 0 :(得分:1)

  

在这个特殊情况下,我认为我们应该摆脱你有4个不完全定制的流的结构。将它们连接在一起将产生链依赖性,如果我们没有实现自己的机制,将难以控制。

我想在这里关注你的目标:

 INPUT >----[read] → [transform0] → [transform1] → [write]-----> OUTPUT
               |          |              |            |
 KILL_ALL------o----------o--------------o------------o--------[nothing to drain]

我相信上述结构可以通过组合自定义来实现:

  1. duplex stream - 使用

  2. 执行自己的_write(chunk, encoding, cb)_read(bytes)
  3. transform stream - 用于自己的_transform(chunk, encoding, cb)实施。

  4.   

    由于您使用的是writable-stream-parallel软件包,因此您可能还需要查看他们的库,因为duplex实现可以在此处找到:https://github.com/Clever/writable-stream-parallel/blob/master/lib/duplex.js。   他们的transform stream实施就在这里:https://github.com/Clever/writable-stream-parallel/blob/master/lib/transform.js。他们在这里处理highWaterMark

    可能的解决方案

    他们的写入流:https://github.com/Clever/writable-stream-parallel/blob/master/lib/writable.js#L189有一个有趣的函数writeOrBuffer,我想你可以稍微调整它来中断从缓冲区写入数据。

    注意:这些3 flags正在控制缓冲区清除:

    ( !finished && !state.bufferProcessing && state.buffer.length )
    

    <强>参考文献: