什麼是流?如何理解流?以下這篇文章就帶大家深入了解Nodejs中的串流(Stream),希望對大家有幫助!
stream 是一個抽象的資料接口,它繼承了EventEmitter,它能夠發送/接受數據,本質就是讓資料流動起來,如下圖:
流不是Node 中獨有的概念,是作業系統最基本的操作方式,在Linux 中| 是Stream,只是Node 層面對其做了封裝,提供了對應的API
先用下面的程式碼建立一個文件,大概在400MB 左右【相關教學推薦:nodejs影片教學】
當我們使用readFile 去讀取的時候,如下程式碼
正常啟動服務時,佔用10MB 左右的記憶體
使用curl http://127.0.0.1:8000
發起請求時,記憶體變成了420MB 左右,和我們建立的檔案大小差不多
#改為使用使用stream 的寫法,程式碼如下
再次發起請求時,發現記憶體只佔用了35MB 左右,相較於readFile 大幅減少
如果我們不採用流的模式,等待大檔案載入完成在操作,會有以下的問題:
總結來說就是,一次性讀取大文件,內存和網路都吃不消
我們讀取檔案的時候,可以採用讀取完成之後在輸出資料
上述說到stream 繼承了EventEmitter 可以是實現監聽數據。首先將讀取資料改為串流讀取,使用on("data", ()⇒{})
接收數據,最後透過on("end", ()⇒{} )
最後的結果
有資料傳遞過來的時候就會觸發data 事件,接收這段資料做處理,最後等待所有的資料全部傳遞完成之後觸發end 事件。
資料是從一個地方流向另一個地方,先看看數據的來源。
http 請求,請求介面來的資料
#console 控制台,標準輸入stdin
file 文件,讀取文件內容,例如上面的範例
source.pipe(dest) ,source 和dest 透過pipe 連接,讓資料從source 流向dest
http 請求,介面請求中的response
#file 文件,寫入檔案
可讀流是對提供資料的來源(source)的抽象
所有的Readable 都實作了stream.Readable 類別定義的介面
? 讀取檔案流建立
fs.createReadStream 建立一個Readable 物件
和暫停模式(pause mode)
,這個決定了chunk 資料的流動方式:自動流動和手工流動在ReadableStream 中有一個_readableState 屬性,在其中有一個flowing 的屬性來判斷流的模式,他有三種狀態值:false:表示為暫停模式
- 监听 data 事件 - 调用 stream.resume 方法 - 调用 stream.pipe 方法将数据发送到 Writable
- 移除 data 事件 - 调用 stream.pause 方法 - 调用 stream.unpipe 移除管道目标
實作原理
當快取區長度為0或小於highWaterMark 這個值得時候就會呼叫_read 去底層取得資料原始碼連結
可寫流Writeable Stream
#可寫入流的特性
透過write 寫入資料
#當寫入資料達到highWaterMark 的大小時,會觸發drain 事件
自訂可寫流
在end 方法呼叫後,當所有底層的寫入操作均完成時,會觸發finish 事件
雙工流Duplex Stream
雙工流,既可讀,也可寫入。實際上繼承了Readable 和Writable 的一種流,那它既可以當做可讀流來用又可以當做可寫流來用
自訂的雙工流需要實作Readable 的_read 方法和Writable 的_write 方法
轉換流Transform Stream
上述的例子中,可讀流中的資料(0/1)和可寫流中的資料('F','B','B')是隔離的,兩者並沒有產生關係,但對於Transform 來說在可寫端寫入的資料經過變換後會自動添加到可讀端。
Transform 繼承於Duplex,並且已經實作了_write 和_read 方法,只需要實作_tranform 方法可以
gulp 基於Stream 的自動化建置工具,看一段官網的範例程式碼
###less → less 轉為css → 執行css 壓縮→ 壓縮後的css#######其實less() 和minifyCss() 都是對輸入的資料做了一些處理,然後交給了輸出資料#########Duplex 和Transform 的選擇###
和上面的範例對比起來,我們發現一個串流同時面向生產者和消費者服務的時候我們會選擇Duplex,當只是對資料做一些轉換工作的時候我們便會選擇使用Tranform
背壓問題來自於生產者消費者模式中,消費者處理速度過慢
比如說,我們下載過程,處理速度為3Mb/s,而壓縮過程,處理速度為1Mb/s,這樣的話,很快緩衝區隊列就會形成堆積
要麼導致整個過程記憶體消耗增加,要麼導致整個緩衝區慢,部分資料遺失
#背壓處理可以理解為一個向上」喊話」的過程
當壓縮處理發現自己的緩衝區資料擠壓超過閾值的時候,就對下載處理“喊話”,我忙不過來了,不要再發了
下載處理收到訊息就暫停向下發送資料
#我們有不同的函數將資料從一個進程傳入另一個進程。在Node.js 中,有一個內建函數稱為 .pipe(),同樣地最終,在這個進程的基本層面上我們有二個互不相關的元件:資料的_源頭_,和_消費者_
當 .pipe() 被來源呼叫之後,它通知消費者有資料需要傳輸。管道函數為事件觸發建立了合適的積壓封裝
在資料快取超出了highWaterMark 或寫入的列隊處於繁忙狀態,.write() 會返回 false
#當 false 返回之後,積壓系統介入了。它將暫停從任何發送資料的資料流中進入的 Readable。一旦資料流清空了,drain 事件將被觸發,消耗進來的資料流
一旦佇列全部處理完畢,積壓機制將允許資料再次發送。在使用中的記憶體空間將自我釋放,同時準備接收下一次的批次資料
我們可以看到pipe 的背壓處理:
更多node相關知識,請造訪:nodejs 教學!
以上是深入淺析Node中的Stream(流)的詳細內容。更多資訊請關注PHP中文網其他相關文章!