Node Stream(流)有哪些?,nodejs全局对象stream
本文将带您了解Node stream模块,并介绍如何使用stream构建高性能的Node.js应用程序。希望对你有帮助!
node.js速度课程简介:进入学习
当你在键盘上输入字符、从磁盘上读取文件或从互联网上下载文件时,一股信息流(位)正流经不同的设备和应用程序。
如果您学会处理这些字节流,您将能够构建高性能和有价值的应用程序。例如,想象一下,当你在YouTube上观看一个视频时,你不必等到完整的视频被下载。一旦有一个小缓冲,视频就开始播放,剩下的就在你看的时候继续下载。
Nodejs包含一个内置的模块流,允许我们处理流数据。在本文中,我们将通过几个简单的例子来解释stream的用法。我们还将描述在构建面对复杂情况的高性能应用程序时,如何构建管道来合并不同的流。
在我们深入理解应用程序的构造之前,理解Node.jstream模块提供的特性是很重要的。
我们开始吧!
Node.js 流的类型
Node.js流提供四种类型的流
可读流、可写流、双工流、转换流让我们从较高的层面来看看每种流类型。
可读流
可读流可以从特定数据源读取数据,最常见的是从文件系统读取数据。Node.js应用程序中其他常见的可读流用法有:
process . stdin-通过stdin读取终端应用程序中的用户输入。Http。incoming message-读取HTTP服务中的传入请求内容或HTTP客户端中服务器的HTTP响应。
可写流
您可以使用可写流将数据从应用程序写入特定位置,如文件。
Process.stdout可用于将数据作为标准输出写入,并由console.log在内部使用
接下来是双工流和转换流,可以定义为基于可读流和可写流的混合流类型。
双工流
双工流是可读流和可写流的组合。它可以将数据写入特定的位置,也可以从数据源读取数据。双工流最常见的情况是net。套接字,用于从套接字读取和写入数据。
重要的是,双工流中可读端和可写端的操作相互独立,数据不会从一端流到另一端。
转换流
转换流与双工流略有相似,但在转换流中,可读端和可写端是相关的。
密码。Cipher类就是一个很好的例子,它实现了加密流。通过密码。密文流,应用程序可以将明文数据写入流的可写端,并从流的可读端读取加密的密文。这种类型的流因其转换性质而被称为转换流。
附注:另一个转换流是stream。直通流。PassThrough将数据从可写端传递到可读端,不进行任何转换。这听起来可能有些多余,但是直通流对于构建定制流和流管道非常有帮助。(例如创建流数据的多个副本)
从可读的 Node.js 流读取数据
一旦可读流连接到生产数据源(如文件),就可以通过流以多种方式读取数据。
首先,创建一个名为myfile的简单文本文件,大小为85字节,包含以下字符串:
这是一个很好的例子。Curabitur nec mauris turpis。现在,让我们看看从可读流中读取数据的两种不同方式。
1. 监听 data 事件
从可读流中读取数据的最常见方式是侦听流发出的数据事件。下面的代码演示了这种方式:
const fs=require(fs )
const readable=fs . createreadstream(。/myfile ,{ high watermark:20 });
readable.on(data ,(chunk)={
console . log(` read $ { chunk . length } bytes \ n $ { chunk . tostring()} \ n `);
})将})highWaterMark属性作为选项传递给fs.createReadStream,该属性用于确定流中缓冲了多少数据。然后数据被发送到读取机制(在本例中,是我们的数据处理器)。默认情况下,可读文件系统流的高水位线值为64kb。我们特意将该值重写为20字节,以触发多个数据事件。
如果运行上面的程序,它将在五次迭代中从myfile中读取85个字节。您将在控制台中看到以下输出:
读取20个字节
Lorem ipsum dolor si
读取20个字节
“时间,约定”
读取20个字节
肥胖精英。诅咒
读取20个字节
你好吗
读取5个字节
Rpis。
2. 使用异步迭代器
从可读流中读取数据的另一种方法是使用异步迭代器:
const fs=require(fs )
const readable=fs . createreadstream(。/myfile ,{ high watermark:20 });
(async ()={
for await(可读的常量块){
console . log(` read $ { chunk . length } bytes \ n $ { chunk . tostring()} \ n `);
}
})()如果您运行这个程序,您将得到与上一个示例相同的输出。
可读 Node.js 流的状态
当侦听器侦听可读流的数据事件时,流的状态将切换到“流动”状态(除非流被显式暂停)。可以通过stream对象的readableFlowing属性检查流的“流动”状态。
我们可以稍微修改前面的示例,并通过数据处理器进行演示:
const fs=require(fs )
const readable=fs . createreadstream(。/myfile ,{ high watermark:20 });
设字节读取=0
console . log(` before attach data 处理程序。正在流动:$ { readable . readable flow } `);
readable.on(data ,(chunk)={
console . log(` read $ { chunk . length } bytes `);
bytesRead=chunk .长度
//从可读流中读取60个字节后停止读取
if (bytesRead===60) {
可读.暂停()
console.log(`暂停后()调用。正在流动:$ { readable . readable flow } `);
//等待1秒后继续读取
setTimeout(()={
可读. resume()
console.log(`after resume()调用。正在流动:$ { readable . readable flow } `);
}, 1000)
}
})
console.log(`附加数据处理程序后。正在流动:$ { readable . readable flow } `);在这个例子中,我们从一个可读的流中读取myfile,但是在读取了60个字节之后,我们将数据流暂停了1秒钟。我们还打印了readableFlowing属性在不同时间的值,以了解它是如何变化的。
如果您运行上面的程序,您将得到以下输出:
在附加“数据”处理程序之前。正在流动:空
附加“数据”处理程序后。正在流动:真的
读取20个字节
读取20个字节
读取20个字节
暂停()调用后。正在流动:假
resume()调用后。正在流动:真的
读取20个字节
读取字节我们可以用它来解释输出:
当我们的程序启动时,readableFlowing的值为null,因为我们没有提供任何机制来消费流。
连接到数据处理器后,可读流变为“流动”模式,readableFlowing变为true。
一旦读取了60个字节,就通过调用pause()来暂停流,readableFlowing也被更改为false。
等待1秒后,通过调用resume(),流再次切换到“流动”模式,readableFlowing更改为true’。然后,文件内容的其余部分在流中流动。
通过 Node.js 流处理大量数据
由于流式传输,应用程序不需要在内存中保存大型二进制对象:可以接收和处理小型数据块。
在这一部分,让我们结合不同的流来构建一个能够处理大量数据的真正的应用程序。我们将使用一个小的实用程序来生成给定文件的SHA-256。
但是首先,我们需要创建一个大的4GB的假文件来测试。您可以用一个简单的shell命令来完成:
MAC OS:MK file-n 4g 4 GB _ file Linux上:XFS _ mkfile4096M4gb_file在我们创建了伪文件4GB _ file之后,让我们在不使用stream模块的情况下生成传入文件的SHA-256哈希。
const fs=require( fs );
const crypto=require( crypto );
fs.readFile(。/4gb_file ,(readErr,data)={
if (readErr)返回console.log
const hash=crypto . create hash( sha 256 )。更新(数据)。digest( base64 );
fs.writeFile(。/checksum.txt ,哈希,(writeErr)={
writeErr console.error(err)
});
});如果运行上述代码,可能会出现以下错误:
range error[ERR _ FS _ FILE _ TOO _ LARGE]:文件大小(4294967296)大于2 GB
在fsreqcallback . readfileafterstat[as on complete](fs . js:294:11){
代码:“错误文件系统文件太大”
}出现以上错误是因为JavaScript运行时无法处理随机的大缓冲区。运行时可以处理的最大缓冲区大小取决于您的操作系统结构。您可以使用内置缓冲模块中的buffer.constants.MAX_LENGTH变量来检查操作系统缓存的最大大小。
即使上面的错误没有发生,在内存中保存大文件也是有问题的。我们拥有的可用物理内存将限制我们的应用程序可以使用的内存量。高内存利用率也会导致应用程序在CPU使用方面的低性能,因为垃圾收集会变得很昂贵。
使用 pipeline() 减少 APP 的内存占用
现在,让我们看看如何修改应用程序以使用流并避免此错误:
const fs=require( fs );
const crypto=require( crypto );
const { pipeline }=require( stream );
const hash stream=crypto . create hash( sha 256 );
hashStream.setEncoding(base64 )
const inputStream=fs . create read stream(。/4gb _ file’);
const output stream=fs . create write stream(。/checksum . txt’);
管道(
输入流,
哈希流,
输出流,
(错误)={
err console.error(错误)
}
)在这个例子中,我们使用由crypto.createHash函数提供的流方法。它返回一个“转换”的流对象hashStream来为随机的大文件生成哈希。
为了将文件内容传输到此转换流中,我们使用fs.createReadStream为4gb_file创建了一个可读流inputStream。我们将hashStream转换流的输出传递到可写流outputStream,checksum.txt由fs.createWriteStream创建
如果你运行上面的程序,你会在checksum.txt文件中看到4GB文件的SHA-256哈希。
对流使用 pipeline() 和 pipe() 的对比
在前面的案例中,我们使用了管道函数来连接多个流。另一种常见的方法是使用。pipe()函数如下:
输入流。管道(哈希流)。pipe(outputStream)但是有几个原因,所以不建议使用。生产应用程序中的管道()。如果其中一个流被关闭或发生错误,pipe()不会自动销毁连接的流,这将导致应用程序内存泄漏。类似地,pipe()不会自动将错误跨流转发到一个地方进行处理。
因为这些问题,就有了pipeline(),所以建议你用pipeline()代替pipe()来连接不同的流。我们可以重写上面的pipe()示例,以使用pipeline()函数,如下所示:
管道(
输入流,
哈希流,
输出流,
(错误)={
err console.error(错误)
}
)pipeline()接受回调函数作为最后一个参数。来自连接流的任何错误都将触发回调函数,因此很容易在一个地方处理错误。
总结:使用 Node.js 流降低内存并提高性能
在Node.js中使用流有助于我们构建能够处理大数据的高性能应用程序。
在本文中,我们将介绍:
Node.js流的四种类型(可读流、可写流、双工流和转换流)。如何通过监听数据事件或使用异步迭代器从可读流中读取数据。使用管道连接多个流以减少内存占用。一个简短的警告:你可能不会遇到太多必须使用流的场景,基于流的方案会增加你应用的复杂度。确保使用流的好处大于复杂性。
更多关于node的信息,请访问:nodejs教程!以上就是说说Node.jstream模块,看看如何构建高性能应用的细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。