Node.js的stream使用簡介

banq發表於2014-12-24
如果你正在學習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);
<p class="indent">


上面的這段程式碼並沒有什麼問題,但是在每次請求時,我們都會把整個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);
<p class="indent">


在這裡,.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);
<p class="indent">


透過上面的程式碼,我們成功的將傳送到瀏覽器端的資料進行了gzip壓縮。我們只是使用了一個oppressor模組來處理這件事情。
一旦你學會使用流api,你可以將這些流模組像搭樂高積木或者像連線水管一樣拼湊起來,從此以後你可能再也不會去使用那些沒有流API的模組獲取和推送資料了。

[該貼被banq於2014-12-24 10:59修改過]

相關文章