Node.js的stream使用简介

如果你正在学习Nodejs,那么Stream一定是一个你需要掌握的概念。如果你想成为一个Node高手,那么Stream一定是武功秘籍中不可缺少的一个部分。由Node高手substack带来的jabez128/stream-handbook · GitHub绝对是经典入门读物之一,其在Github上的star数量以上超过了4500个。

安装:npm install -g stream-handbook


在编写代码时,我们应该有一些方法将程序像连接花园水管一样连接起来 -- 这是传送消息的另外一种方式。这也应该是IO应有的方式。

Stream从从unix开始就已经证明是一种将小组件组合成大型系统的可靠方式,在unix中,我们可以使用“|”符号来实现流。在node中,node的内建stream模块已经被核心模块和其他用户自定义的模块使用。和unix类似,node中的流模块的基本操作符是:.pipe(),这样你就有了一个后压机制来应对那些对数据消耗较慢的对象。

Stream能帮助你分离关注,因为他们将实现抽象为一个一致的可重用的接口,你可以将一个流的输出口接到另一个流的输入口,然后使用使用一些库来对流实现高级别的控制。

在node中,I/O都是异步的,所以在和硬盘以及网络的交互过程中会涉及到传递回调函数的过程。你之前可能会写出这样的代码:


var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
fs.readFile(__dirname + '/data.txt', function (err, data) {
res.end(data);
});
});
server.listen(8000);

上面的这段代码并没有什么问题,但是在每次请求时,我们都会把整个data.txt文件读入到内存中,然后再把结果返回给客户端。想想看,如果data.txt文件非常大,在响应大量用户的并发请求时,程序可能会消耗大量的内存,这样很可能会造成用户连接缓慢的问题。

其次,上面的代码可能会造成很不好的用户体验,因为用户在接收到任何的内容之前首先需要等待程序将文件内容完全读入到内存中。

所幸的是,(req,res)参数都是流对象,这意味着我们可以使用一种更好的方法来实现上面的需求:


var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
var stream = fs.createReadStream(__dirname + '/data.txt');
stream.pipe(res);
});
server.listen(8000);

在这里,.pipe()方法会自动帮助我们监听data和end事件。上面的这段代码不仅简洁,而且data.txt文件中每一小段数据都将源源不断的发送到客户端。

除此之外,使用.pipe()方法还有别的好处,比如说它可以自动控制后端压力,以便在客户端连接缓慢的时候node可以将尽可能少的缓存放到内存中。

想要将数据进行压缩?我们可以使用相应的流模块完成这项工作!


var http = require('http');
var fs = require('fs');
var oppressor = require('oppressor');

var server = http.createServer(function (req, res) {
var stream = fs.createReadStream(__dirname + '/data.txt');
stream.pipe(oppressor(req)).pipe(res);
});
server.listen(8000);

通过上面的代码,我们成功的将发送到浏览器端的数据进行了gzip压缩。我们只是使用了一个oppressor模块来处理这件事情。
一旦你学会使用流api,你可以将这些流模块像搭乐高积木或者像连接水管一样拼凑起来,从此以后你可能再也不会去使用那些没有流API的模块获取和推送数据了。
[该贴被banq于2014-12-24 10:59修改过]