[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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...