最近學習使用Node.js創建http proxy server,少不了要跟Stream打交道。昨天開始查閱一些資料,多少有了一些粗淺了解。整理在這里,供學習之用。
從Node.js API文檔中可知,
"A stream is an abstract interface implemented by various objects in Node. For example a request to an HTTP server is a stream, as is stdout. Streams are readable, writable, or both. All streams are instances of EventEmitter。""流是很多I/0操作的抽象,被 Node 中的很多對象所實現。比如對一個 HTTP 服務器的請求是一個流(可讀流)(服務器的響應是一個流(可寫流)),stdout也是流。流是可讀、可寫或兼具兩者的。所有流都是 EventEmitter 的實例。"
一. 為什么需要流(Stream)?
舉個例子,如果要讀取一個文件,一次性讀取需要占用大內存,是不可取的。因此就有了流,用流會很方便,可以幫我們避免這樣的問題,調用其接口不用關心底層如何實現。
二. 什么是流(Stream)?
流(Stream)是可讀,可寫或雙工的。可以通過require('stream')加載流的基類,其中包括四類流, Readable 流、Writable 流、Duplex 流和Transform 流的基類。
另外如果覺得上述四類基類流不能滿足需求,可以編寫自己的擴充類流。像我們Team現在正做的Node項目,就重寫了Transform類以供使用。
按照官方的API文檔,步驟如下:
- 在您的子類中擴充適合的父類。(例如util.inherits(MyTransform, Transform); )
- 在您的構造函數中調用父類的構造函數,以確保內部的機制被正確初始化。
- 實現一個或多個特定的方法,參見下面的細節。
三. Readable流(可讀流)介紹
Readable(可讀)流接口是對您正在讀取的數據的來源的抽象。換言之,數據出自一個可讀流。
Readable 流有兩種“模式”:流動模式和暫停模式。
當處於流動模式時,數據由底層系統讀出,並盡可能快地提供給您的程序;當處於暫停模式時,您必須明確地調用 stream.read()
來取出若干數據塊。流默認處於暫停模式。
A. 通過以下三種方法,可讀流會被切換到流動模式
1. 添加一個'data'事件處理器來監聽數據。
2. 調用 resume()方法來明確開啟數據流。
3. 調用 pipe()方法將數據發送到一個可寫流(Writable)。
之前我一直對pipe()方法有疑問,不清楚其用法。現在了解,當我們用pipe()為可讀流指定了一個接受者(可寫流)的時候,數據才會真正的被從底層系統讀出,傳遞給可寫流。
B. 下面介紹Readable流有以下幾種事件
1. 'Readable'事件
2. 'data'事件 - 數據正在傳遞時,觸發該事件(以chunk數據塊為對象)
3. 'end'事件 - 數據傳遞完成后,會觸發該事件。
4. 'close'事件
5. 'error'事件
所有這些事件都可以在官方API文檔中找到例子。
C. 下面介紹Readable流很重要的一個方法,pipe()方法。
該方法從可讀流中拉取所有數據,並寫入到所提供的目標(可寫流)。該方法能自動控制流量以避免目標被快速讀取的可讀流所淹沒。
值得注意的是,默認情況下,當數據傳送完畢,觸發'end'事件時,會同時觸發目標(可寫流)的'end'事件,導致目標不再可寫。
舉個簡單的小例子,
1 //http.js 2 3 var http = require('http'); 4 var fs = require('fs'); 5 6 http.createServer(function(req, res){ 7 var stream = fs.createReadStream(__dirname + '/data.txt'); 8 stream.pipe(res); 9 }).listen(3000); 10 11 console.log('now we are listening 3000 port');
data.txt文件內容如下:
當執行此段代碼后,用戶訪問http://127.0.0.1:3000/,會得到如下響應:
此時,創建此Server后,用戶訪問請求過來,Server會創建一個可讀流,當調用stream.pipe(res)為可讀流指定目標后,可讀流stream會開始從文件data.txt中讀取數據,數據寫入res(可寫流)完畢后,自動調用res的end()方法,結束響應,可寫流不再寫入。
四. Writable流(可寫流)介紹
Writable(可寫)流接口是對寫入數據的目標的抽象。
可寫流重要的兩個方法,
1. write()方法
該方法向底層系統寫入數據,並在數據被處理完畢后調用所給的回調。
2. end()方法
當不再寫入數據時,調用該方法,停止寫入。在調用end()后,再調用write()方法會產生錯誤。
五. 參考資料
1. Node.js官方API文檔
http://www.nodejs.org/api/stream.html
2. 官方API文檔中文版
http://nodeapi.ucdok.com/#/api/stream.html
3. Node 中的流(Stream)
http://blog.segmentfault.com/xingrz/1190000000357044
4. Node Streams: How do they work?
http://maxogden.com/node-streams.html
拋磚引玉,繼續加油。
Best Regards
Kevin Song
- 2014/6/18