当输入流管道传输到多个输出流时,缓冲区级别会发生啥?

Posted

技术标签:

【中文标题】当输入流管道传输到多个输出流时,缓冲区级别会发生啥?【英文标题】:What would happens on buffer level when an input stream piping to multi output streams?当输入流管道传输到多个输出流时,缓冲区级别会发生什么? 【发布时间】:2019-08-02 11:11:59 【问题描述】:

我正在阅读流文档并在https://nodejs.org/api/stream.html#stream_buffering寻找有关流的缓冲行为描述

文档似乎没有提到 inputStream 缓冲区(或缓冲区?)会发生什么,当管道到多个输出时,因为不同的输出具有不同的消耗速度:

在管道多个输出时,可读流是否为每个输出保留一个专用缓冲区?

消耗时输出保持相同的速度还是更快结束?

const input = fs.createReadStream('img.jpg');
const target1 = input.pipe(fs.createWriteStream('target1.jpg'));
const target2 = input.pipe(fs.createWriteStream('target2.jpg'));

【问题讨论】:

【参考方案1】:

TL;DR:简短的回答是 - 较慢的目标流控制流速。

首先让我们看看读取端发生了什么。

const input = fs.createReadStream('img.jpg');

当您实例化输入流时,它会在暂停模式下创建并计划读取(没有同步完成读取,因此它不会访问文件)。该流将highWaterMark 设置为16384 之类的值,并且当前具有0 字节的缓冲区。

const target1 = input.pipe(fs.createWriteStream('target1.jpg'));
const target2 = input.pipe(fs.createWriteStream('target2.jpg'));

现在,当您将其实际通过管道传输到可写流时,通过在pipe method implementation - see the source 中添加on('data') 事件处理程序来设置流动模式。

完成此操作后,我假设没有更多程序要运行,因此节点开始实际读取并在上面的处理程序中运行计划的代码,该处理程序只是写入通过的任何数据。

当任何目标要写入的数据多于其highWaterMark 时,就会发生流控制,这会使write 操作返回false。然后由calling pause here in the code 停止读取。在这上面的两行你会看到 state.awaitDrain 增加了。

现在读取流又是paused,而可写流正在将字节写入磁盘——在某些时候,缓冲区级别再次低于highWaterMark。此时会触发drain 事件executes this line 并在调用所有等待的排水管 后恢复流程。这是通过检查递减的awaitDrain 属性是否达到零来完成的,这意味着所有等待的排水事件都已被调用。

在上述情况下,两个流中较快的一个可能会在写入时返回一个虚假值,但它肯定会作为第一个流走。如果不是awaitDrain,更快的流将恢复数据流,这可能会导致两者中较慢的缓冲区溢出。

【讨论】:

以上是关于当输入流管道传输到多个输出流时,缓冲区级别会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

通过 es.map() 和 JSONStream.stringify() 将 JSONStream.parsed() 数据传输到文件流时,节点堆耗尽

流缓冲是啥意思?

C中的管道,用于读取标准输入的缓冲区

当我将任何命令通过管道传输到 telnet 会话时会发生啥,为啥会话会关闭

我可以将多个 ffmpeg 输出通过管道传输到不同的管道吗?

java.io.PipedInputStream