[Node.js] Node.js中的流
原文地址:http://www.moye.me/2015/03/29/streaming_in_node/
什么是流?
说到流,就涉及到一个*nix的概念:管道——在*nix中,流在Shell中被实现为可以通过 |(管道符) 进行桥接的数据,一个进程的输出(stdout)可被直接作为下一个进程的输入(stdin)。
在Node中,流(Stream)的概念与之类似,代表一种数据流可供桥接的能力。
pipe

流化的精髓在于 .pipe()方法。可供桥接的能力,在于数据流的两端(上游/下游 或称为 读/写流)以一个 .pipe()方法进行桥接。
伪代码的表现形式为:
//上游.pipe(下游)
Readable.pipe(Writable);
流的分类
这里并不打算讨论所谓的Node v0.4 之前的“经典”流。那么,流分为这么几类(皆为抽象接口:
- stream.Readable 可读流(需要实现_read方法,关注点在于对数据流读取的细节
- stream.Writable 可写流(需要实现_write方法,关注点在于对数据流写入的细节
- stream.Duplex 可读/写流(需要实现以上两接口,关注点为以上两接口的细节
- stream.Transform 继承自Duplex(需要实现_transform方法,关注点在于对数据块的处理
简单来说:
- .pipe() 的拥有者一定具备 Readable 流(并不局限于)能力,它拥有 'readable'/'data'/'end'/'close'/'error' 一系列事件可供订阅,也提供 .read()/.pause()/.resume()等一系列方法供调用;
- .pipe() 的参数一定具备Writable 流(并不局限于 )能力,它拥有 'drain'/'pipe'/'unpipe'/'error'/'finish' 事件可供访问,也提供 .write()/.end() 等一系列方法供调用
什么鬼
有没有一丝丝焦虑?别急,做为一个说人话的低级码工,我会把Stream掰开了和您扯一扯的。
Stream类,在 Node.js的源码 里,是这么定义的:
var EE = require('events').EventEmitter;
var util = require('util');
util.inherits(Stream, EE);
function Stream() {
EE.call(this);
}
可以看出,本质上,Stream是一个EventEmitter,那意味着它具备事件驱动的功能(.emit/.on...)。众所周知,“Node.js 就是基于V8的事件驱动平台”,实现了事件驱动的流式编程,具备了和Node一样的异步回调的特征。
比如在 Readable 流中,有一个 readable 事件,在一个暂停的只读流中,只要有数据块准备好可读时,它就会被发送给订阅者(Readable 流有哪些呢?express中的 req,ftp或者mutli-form上传组件的req.part,系统中的标准输入 process.stdin等)。有了readable 事件,我们可以做个处理shell 命令输出的分析器之类的工具:
process.stdin.on('readable', function(){
var buf = process.stdin.read();
if(buf){
var data = buf.toString();
// parsing data ...
}
});
这样调用:
head -10 some.txt | node parser.js
对于 Readable 流,我们还可以订阅它的 data 和 end 事件,以获取数据块并在流枯竭时获得通知,如 经典socket示例 中那样:
req.on('connect', function(res, socket, head) {
socket.on('data', function(chunk) {
console.log(chunk.toString());
});
socket.on('end', function() {
proxy.close();
});
});
Readable流状态的切换
需要注意的是,Readable 流有两种状态:flowing mode(激流) 和 pause mode(暂停)。前者根本停不下来,谁被pipe上了就马上不停的给;后者会暂停,直到下游显式的调用 Stream.read() 请求才读取数据块。Readable 流初始化时是 pause mode的。
这两种状态可以互为切换的,其中,
有以下任一行为,pause 转 flowing:
- 对 Readable 流添加一个data事件订阅
- 对 Readable 调用 .resume() 显式开启flowing
- 调用 Readable 流的 .pipe(writable) ,桥接到一个 Writable 流上
有以下任一行为,flowing 转回 pause:
- Readable 流还没有 pipe 到任何流上,可调 .pause() 暂停
- Readable 流已经 pipe 到了流上,需 remove 掉所有 data 事件订阅,并且调用 .unpipe()方法逐一解除与下游流的关系
妙用
结合流的异步特性,我可以写出这样的应用:直接将 用户A 的输出桥接到 用户B 的页面上输出:
router.post('/post', function(req, res) {
var destination = req.headers['destination']; //发给谁
cache[destionation] = req;
//是的,并不返回,所以最好是个ajax请求
});
用户B请求的时候:
router.get('/inbox', function(req, res){
var user = req.headers['user'];
cache.find(user, function(err, previousReq){ //找到之前存的req
var form = new multiparty.Form();
form.parse(previousReq); // 有文件给我
form.on('part', function (part) {
part.pipe(res); //流式大法好:)
part.on('error', function (err) {
console.log(err);
messaging.setRequestDone(uniqueID);
return res.end(err);
});
});
});
});
参考
- how to write node programs with streams: stream-handbook
更多文章请移步我的blog新地址: http://www.moye.me/
[Node.js] Node.js中的流的更多相关文章
- ASP.NET Core SignalR中的流式传输
什么是流式传输? 流式传输是这一种以稳定持续流的形式传输数据的技术. 流式传输的使用场景 有些场景中,服务器返回的数据量较大,等待时间较长,客户端不得不等待服务器返回所有数据后,再进行相应的操作.这时 ...
- node.js中stream流中可读流和可写流的使用
node.js中的流 stream 是处理流式数据的抽象接口.node.js 提供了很多流对象,像http中的request和response,和 process.stdout 都是流的实例. 流可以 ...
- 理解 Node.js 中 Stream(流)
Stream(流) 是 Node.js 中处理流式数据的抽象接口. stream 模块用于构建实现了流接口的对象. Node.js 提供了多种流对象. 例如,对 HTTP 服务器的request请求和 ...
- Node.js数据流Stream之Readable流和Writable流
一.前传 Stream在很多语言都会有,当然Node.js也不例外.数据流是可读.可写.或即可读又可写的内存结构.Node.js中主要包括Readable.Writable.Duplex(双工)和Tr ...
- 极简 Node.js 入门 - 4.5 双工流
极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...
- 在Sublime Text 3 中安装SublimeLinter,Node.js进行JS&CSS代码校验
转载自:http://www.wiibil.com/website/sublimelinter-jshint-csslint.html 在Sublime Text中安装SublimeLinter,No ...
- node.js获取url中的各个参数
实例代码test.js var http=require('http'); var url=require('url'); var querystring=require('querystring') ...
- Node.js数据流Stream之Duplex流和Transform流
Duplex流一个很好的例子是TCP套接字连接.需要实现_read(size)和_Write(data,encoding,callback)方法. var stream = require('stre ...
- Elasticsearch.js 发布 —— 在Node.js和浏览器中调用Elasticsearch(1)
继PHP.Ruby.Python和Perl之后,Elasticsearch最近发布了Elasticsearch.js,Elasticsearch的JavaScript客户端库.可以在Node.js和浏 ...
随机推荐
- switch 的一些事
switch后面的括号的表达式,其值得 “类型" 应为整数类型(包括字符类型). case后面跟一个常量或者常量表达式,
- chrome插件开发-消息机制中的bug与解决方案
序言 最近开发chrome插件,涉及到消息传递机时按照教程去敲代码,结果总是不对.研究了大半天终于找到原因,现在记录下. 程序 插件程序参考官网 chrome官网之消息传递机制, 不能FQ的同事也可以 ...
- Linux之源码包安装软件
安装准备 安装c语言编辑器 gcc 压缩包 node-v6.2.0-linux-x64.tar.gz 源码包保存位置 /usr/local/src/ 源码包安装位置 /us ...
- 生产环境下实践DDD中的规约模式
最近的开发工作涉及到两个模块“任务”和“日周报”.关系是日周报消费任务,因为用户在写日周报的时候,需要按一定的规则筛选当前用户的任务,作为日周报的一部分提交.整个项目采用类似于Orchard那种平台加 ...
- Actor的原理
先从著名的c10k问题谈起.有一个叫Dan Kegel的人在网上(http://www.kegel.com/c10k.html)提出:现在的硬件应该能够让一台机器支持10000个并发的client.然 ...
- Unity3D音乐音效研究-MIDI与波表
其实音乐音效这个命题本身没什么好研究的. Unity3D提供了丰富的结构和使用方式,足够使用了. 但是我有一些小小的想法和需求,一般的Unity资料并没有给我答案. 一个是容量要小.MP3.OGG的高 ...
- 记录排查解决Hubble.Net连接Oracle数据库建立镜像库数据丢失的问题
起因 前几天在弄Hubble连接Oracle数据库,然后在mongodb中建立一个镜像数据库; 发现一个问题,原本数据是11W,但是镜像库中只有6w多条; 刚开始以为是没运行好,又rebuild了一下 ...
- 爱上MVC系列~过滤器实现对响应流的处理
回到目录 MVC的过滤器相信大家都用过,一般用来作权限控制,因为它可以监视你的Action从进入到最后View的渲染,整个过程ActionFilter这个过滤器都参与了,而这给我们的开发带来了更多的好 ...
- EF架构~为导航属性赋值时ToList()的替换方案
回到目录 今天在进行EF开发时,遇到一个问题,在进行join查询时,类中的一个集合类型的导航属性,在给它赋值时,将查询出来的结果ToList()后,出错了,linq to entity不支持这种操作, ...
- Piwik延迟加载脚本
piwik有时候会卡页面,为了不影响页面,可以将Piwik代码放到最后加载 <!-- Piwik --> <script type="text/javascript&quo ...