[NodeJS] Streams流式數據處理

来源:https://www.cnblogs.com/feixianxing/p/18290492/node-js-streams
-Advertisement-
Play Games

本文介紹了NodeJS中流(Stream)的概念、類型和應用。流通過將數據分成小塊進行處理,優化了記憶體使用和數據處理效率。文章涵蓋了四種基本流類型:可讀流、可寫流、雙工流和轉換流,並通過實例代碼演示瞭如何使用流進行高效的數據傳輸和處理。 ...


在現代應用開發中,數據處理的效率和資源管理尤為重要。NodeJS作為一種高效的JavaScript運行時環境,通過其強大的流(Stream)功能,提供了處理大規模數據的便捷方式。流式數據處理不僅能夠優化記憶體使用,還可以提高數據處理的實時性和效率。下文將介紹NodeJS中的流概念、流的類型以及如何利用流來進行數據傳輸和處理。

流的基本概念

流式數據的特點是將數據分成一個一個的chunk,每次操作只針對其中的一小部分。

因此流式數據的讀寫操作不需要將整個數據保存在記憶體中(處理完就丟掉)。

常用於視頻這種包含大量數據的應用場景,也可以在時間和空間角度上更有效地處理數據:

  • 時間:從開始讀到流就可以處理數據並反饋給用戶了,不需要等待全部數據到達,例如:ChatGPT的回答,就是流式數據傳輸,一個字一個字地顯示出來;
  • 空間:如上文所說,在某些場景下不需要將整個數據保存在記憶體中。

NodeJS提供的API

NodeJS中的node:stream模塊提供了對流數據進行處理的抽象介面。

NodeJS中的所有流對象都可以監聽和觸發事件,都是EventEmitter的實例對象。

下麵的表格列出了每一種基本流常用且重要的事件

NodeJS中有四種基本的流類型:可讀流、可寫流、雙工流和轉換流。

描述 案例 事件 方法
可讀流 Readable Streams 可用於讀(消費)數據 1. http request
2. fs read streams
data
end
pipe()
read
可寫流 Writable Streams 可用於寫(生產)數據 1. http responses
2. fs write streams
drain
finish
write()
end()
雙工流 Duplex Streams 可讀可寫 net 網路套接字
轉換流 Transform Streams 雙工流,在讀寫的時候可修改 zlib Gzip creation

流式數據傳輸案例

簡介:創建一個比較大的文本文件,使用NodeJS啟動一個服務,介面分別以三種方法返迴文件內容。

代碼

方法一 不使用流

讀取整個文件的內容之後再返回;

讀取大文件的時候不推薦這樣寫,因為整個文件會先被完整地從磁碟讀取到記憶體中,再返回給客戶端。

import fs from 'node:fs';
import http from 'node:http';

const server = http.createServer();

server.on('request', (req, res)=>{
  // CORS
  res.setHeader('Access-Control-Allow-Origin', '*');

  // Solution 1
  fs.readFile('test.txt', (err, data)=>{
    if(err)console.log(err);
    res.end(data);
  });
});

server.listen(3000, ()=>{
  console.log('listening...');
});
方法二 可讀流

使用可讀流,優點是邊讀文件邊返回,只有當前處理的chunk會占據記憶體;

import fs from 'node:fs';
import http from 'node:http';

const server = http.createServer();

server.on('request', (req, res)=>{
  // CORS
  res.setHeader('Access-Control-Allow-Origin', '*');

  // Solution 2: Streams
  const readable = fs.createReadStream('test.txt');
  readable.on('data', (chunk)=>{
    res.write(chunk);
  });
  readable.on('end', ()=>{
    res.end();
  });
  readable.on('error', (err)=>{
    console.log(err);
    res.statusCode = 500;
    res.end('File reading error!');
  });
});

server.listen(3000, ()=>{
  console.log('listening...');
});
backpressure

這裡介紹一下流控(Flow Controll)領域中的一個名詞:Backpressure(翻譯為 反壓/背壓)。

在Node.js和其他流處理系統中,backpressure(反壓/背壓)是指生產者生成數據的速度超過消費者處理數據的速度時產生的一種控制機制。

當可讀流(Readable Stream)讀取數據的速度快於可寫流(Writable Stream)寫入數據的速度時,就會產生backpressure。為了防止這種情況,可讀流會根據可寫流的消費能力進行控制,暫停或減慢讀取數據的速度。

具體機制

  1. 可寫流的緩衝區:可寫流內部有一個緩衝區,用於暫存數據。如果這個緩衝區被填滿,流會返回 false,表示消費者已經無法及時處理更多的數據。
  2. 暫停和恢復:當可寫流返回 false 時,可讀流會暫停讀取數據。只有在可寫流的緩衝區有足夠的空間後,可讀流才會恢復讀取。
  3. 事件驅動:Node.js 流通過事件驅動的方式處理backpressure。當可寫流的緩衝區有空間時,會觸發 drain 事件,通知可讀流繼續讀取數據。

示例代碼:通過手動暫停和恢複合理利用緩衝區,避免數據丟失、記憶體溢出和資源耗盡。

import fs from 'node:fs';

const readableStream = fs.createReadStream('input.txt');
const writableStream = fs.createWriteStream('output.txt');

readableStream.on('data', (chunk)=>{
  const canWrite = writableStream.write(chunk);
  // 可寫流的緩衝區空間不夠了,暫停讀數據(生產)
  if(!canWrite){
    readableStream.pause();
  }
});

// 當可寫流的緩衝區空間足夠,會觸發`drain`事件
// 可以繼續讀數據
writableStream.on('drain', ()=>{
  readableStream.resume();
});

// 讀取結束,停止寫入
readableStream.on('end', ()=>{
  writableStream.end();
  console.log('done.');
});
pipe

在 Node.js 中,pipe 方法提供了一種更簡單和自動化的方式來處理流之間的 backpressure 問題。pipe 方法可以連接可讀流和可寫流,並自動處理 backpressure,無需手動暫停和恢復流。

示例代碼

import fs from 'node:fs';

const readableStream = fs.createReadStream('input.txt');
const writableStream = fs.createWriteStream('output.txt');

// 統一錯誤處理函數
function handleError(err) {
  console.error('發生錯誤:', err);
}

// 使用 pipe 連接可讀流和可寫流,並處理錯誤
readableStream.pipe(writableStream)
  .on('error', handleError);

// 處理可讀流和可寫流的錯誤
readableStream.on('error', handleError);
writableStream.on('error', handleError);

語法是:

readableSource.pipe(writableDestination);

接下來回到上文的關於流式數據網路傳輸的案例。

方法三 pipe

使用pipe可以簡化許多代碼,核心代碼就是

readable.pipe(res);

示例代碼:

import fs from 'node:fs';
import http from 'node:http';

const server = http.createServer();

server.on('request', (req, res)=>{
  // CORS
  res.setHeader('Access-Control-Allow-Origin', '*');

  // Solution 3: Pipe
  const readable = fs.createReadStream('test.txt');
  readable.pipe(res).on('error', ()=>{
    res.statusCode = 500;
    res.end('File reading error!');
  });
});

server.listen(3000, ()=>{
  console.log('listening...');
});

總結

  • 流(Stream)在NodeJS中的工作原理是將數據分成一個個小塊進行處理,這樣無需將整個數據載入到記憶體中,從而優化了記憶體使用和數據處理效率。
  • 流在NodeJS中有四種基本類型:可讀流、可寫流、雙工流和轉換流,每種類型都有其特定的應用場景和事件機制。
  • 流的應用場景主要包括視頻播放、文件處理、實時數據傳輸等。在這些場景中,流通過邊讀邊寫、邊處理邊傳輸的方式,可以有效地提高數據處理的實時性和系統的性能。

參考

[1] B站 - NodeJS教程
[2] 知乎 - 如何形象的描述反應式編程中的背壓(Backpressure)機制?


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • UIButton用於創建可交互的按鈕。按鈕可以響應用戶的觸摸事件,執行特定的動作或邏輯。 創建和配置UIButton 創建UIButton的基本步驟: // 創建UIButton實例,指定按鈕類型為系統類型 UIButton *button = [UIButton buttonWithType:UI ...
  • 在Objective-C進行iOS開發中,UILabel是一個非常基礎且常用的UI組件,用於在應用界面上顯示一段靜態文本。UILabel屬於UIKit框架的一部分,提供了豐富的屬性來控制文本的顯示方式,包括文本內容、字體、顏色、對齊方式、行數等。 創建和配置UILabel 創建一個UILabel實例 ...
  • 在iOS開發中,UITableView和UICollectionView是兩個非常核心的用於展示集合數據的UI組件。它們都能以列表的形式展示數據,但各自的特點和使用場景有所不同。 UITableView UITableView用於展示和管理垂直滾動的單列數據列表。它是以行的形式展示數據,每行(cel ...
  • 咱們國內現在手機分為兩類,Android手機與蘋果手機,現在用的各類APP,為了手機的使用安全,避免下載到病毒軟體,官方都極力推薦使用手機自帶的應用商城進行下載,但是國內Android手機品類眾多,手機商城各式各樣,做不到統一,所以Android的APP上架得一個一個平臺去申請上架,一直讓開發人員頭... ...
  • UINavigationController 是 iOS 中用於管理視圖控制器層次結構的一個重要組件,通常用於實現基於堆棧的導航。它提供了一種用戶界面,允許用戶在視圖控制器之間進行層次化的導航,例如從列表視圖到詳細視圖。 UINavigationController 的主要功能 管理視圖控制器堆棧: ...
  • UITabBarController 是 iOS 中用於管理和顯示選項卡界面的一個視圖控制器。它允許用戶在多個視圖控制器之間進行切換,每個視圖控制器對應一個選項卡。 主要功能 管理多個視圖控制器: UITabBarController 管理一個視圖控制器數組,每個視圖控制器對應一個選項卡。 顯示選項 ...
  • 在MVC模型中,V指view,負責用戶界面的顯示、處理用戶輸入,並將輸入傳遞給控制器。C是指ViewController,充當模型和視圖之間的中介。控制器接收用戶輸入,處理用戶請求,並將結果傳遞給視圖以更新顯示。本文詳細介紹在iOS開發中UIView與UIViewController的生命周期。 U ...
  • ‍ 寫在開頭 點贊 + 收藏 學會 理解 forEach JavaScript 的forEach方法是一種流行的數組迭代工具。它為每個數組元素執行一次提供的函數。但是,與傳統的for 和 while迴圈不同,forEach它被設計為對每個元素執行該函數,沒有內置機制來提前停止或中 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...