Photo by Elevate on Unsplash

換個角度用生活思考難以理解的串流緩衝區 (Stream + Buffer)

Whien
6 min readSep 15, 2018

串流 Stream 一直是我開發到現在以來,一直似懂非懂的但卻超級有興趣的東西,反反覆覆看了好幾次也不確定是不是完全能夠理解,透過紀錄此篇文章再次讓自己留下今年的理解,看看是否明年也是如此,不提實作,此文章只釐清概念。

Nodebp April 2014: The History of Node.js Streams

解決的問題

文字理解

首先使用前要先知道位什麼要使用,還有使用場景,這是最重要的。

在資料傳輸與取得的過程中,資料就是一份 Date,而對於程式來說,不可能直接的就知道我們要傳什麼文件,文件有什麼內容,所以必須先把 Data 內容取得後,暫時存放起來,而通常存放的地方,我們就稱之為「Buffer」。

顧名思義 Buffer 是一個緩衝區,這個緩衝區會先將需要的 Data 暫時儲存在一個地方,可以想像可能就是一個「Memory」中的變數。

生活理解

如何用生活來帶入這樣的解釋呢?想像今天在 Medium 上面想要發表一篇文章,這篇文章在「尚未送出」前,這群文字就是處於一個 Buffer 的狀態,也就是被放在一塊空白輸入框中,這個輸入框的內容什麼時候會被送出 Buffer 本身不知道,但最後當完成時,當點擊了 Publish 那麼這篇文章就被送出了,這時候 Buffer 中就會清空,等待下次的使用。

產生什麼問題

至於這樣一來,Buffer 聽起來很厲害,但發生了什麼樣的問題呢?就是當一篇文章檔案內容太大時,就必須花費許多的時間來做傳輸,但是傳輸不打緊,問題在於「接收方」來說,其實也是必須要有一個 Buffer 來接收的內容,不然怎麼能夠知道文章給了什麼呢?

但這是問題嗎

可是會發現,好像每次傳送時都沒什麼問題,Buffer 都可以好好的接到完整內容,然後做後續的「內容儲存」等等的啊!

正視問題

那是因為現在的網路傳輸已經越來越快,頻寬越來越大,一次允許的資料量也是越來越多,但思考平常一個文章可能「字數」都還說得過去,上萬個字已經是很多的量了,但這對於一次性傳輸或一次性接收都不是問題,但如果今天是個「影片」或「音樂」呢?

而且對於使用者的體驗來說也不是好的,如果想要在「尚未接收完全」時就讓使用者可以看到影片跟聽到音樂甚至可以 Live 直播,這些都是依賴了串流(Stream)這樣的一個技術演進。

兩大主要角色 + Buffer + EventEmitter

  • Buffer
  • Event
  • Readable Stream
  • Writable Stream

緩衝區(Buffer)

前面提到了關於 Data 與 Buffer 的關係,因為 Buffer 量太多,造成了傳輸上的困境,而延伸出了如何(控制 Buffer)這樣的一門解決方案。

事件(EventEmitter)

事件在 JavaScript 中是個優秀的代表,透過 EventEmitter 可以使得每個動作給予一個命令的支配,當什麼事情發生了或接收到了什麼事情就做某件事,這是 EventEmitter 很重要的精髓所在,而 JavaScript Stream 中就是 EventEmitter 也用到了大量的 EventEmitter 來讓工作流程有如流水般的順暢。

讀取流(Readable Stream)

讀取流負責的事情就是將讀取進來以後的每筆 Data 都視為一個 Chunk 並且儲存下來後,丟到 Buffer 中做暫存,等到儲存到了一定的量,就發送到「需要的人」手上,清除 Buffer,然後就這樣一直反覆接收 Chunk,丟到 Buffer,接收 Chunk,丟到 Buffer … 直到檔案內容被需要的人消耗完。

寫入流(Writable Stream)

寫入流負責將 Readable 送來的資料接下來,然後寫到最終要儲存的地方,可能是一個 MP4 影片檔,而接收的過程跟讀取相同,可以放到 Buffer 中做暫存在被處理又或是接到 Chunk 就直接送到 File 中儲存起來也是可以。

背壓(Backpressure)

背壓是一個工程上的用詞,在電腦系統方面解釋(一種系統資源監視功能,偵測環系統資源,例如硬碟空間及記憶體有過度),簡單說就是控制現在可乘載的壓力來控制資源的接收量。

Stream Buffer 處理與傳輸

  • pipe

處理 readable 與 writable 的運輸方式是透過 pipe,pipe 是一個可以串接的方法,例如 fs.read(‘path’).pipe(transform).pipe(res),像個工作的 SOP 管線,直到所有的 Buffer 都被處理完畢。

Stream 延伸

此兩種作法就是透過 Readable 與 Writable 的延伸,讓使用上可以更抽象更方便撰寫,有興趣可以從參考文章中更深入了解。

  • Transform Stream
  • Duplex Stream

最後生活理解

*通常使用 Stream 是從伺服器或某一方拿取一份資源。

從一份文章的內容

如果要將一份 Medium 文章傳給閱讀者但不要讓這篇文章一次傳輸太多,這時候就像是 mediumPost.pipe(最後要到達的地點),然後 Readable 就會每次擷取 20 個中文字,然後送到「最後要到達的地點」,一直傳一直傳,直到文章整份都傳完為止

最後要到達的地點

知道了有一份文字內容要來了,於是後端就會準備好一個桶子(Buffer)來裝每次進來的 chunk 然後做顯示的處理,直到 chunk 傳送結束。

我是懷恩,目前是一位 YOSGO 的核心開發,在一個我熱愛的新創團隊活躍著,並做一位核心開發者,我熱愛開發與分享「前端專注在瀏覽器的相關開發、效能與互動體驗」
「後端專注在網頁伺服器的效能調適與架構的配置」
「對於網頁技術有著熱衷,隨時跟在新技術的旁邊」
如果您覺得文章有幫助到您,請不吝嗇給我一個手掌
讓我們做一個「Give me five」然後就繼續一起往更美好的開發大道前進吧
如果您有任何問題,歡迎與我聯繫,我也時常在自己 FB 上開直播做紀錄
Github: https://github.com/madeinfree
FB:https://www.facebook.com/haowei.liou
Email: sal95610@gmail.com

參考資訊

--

--

Whien

遨遊在硬體與軟體世界中,對於計算機一切事物都充滿好奇及熱情。