国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

用Node.js編寫內存效率高的應用程序

開發 前端
在本文中,我們通過解決一個問題來了解 NodeJS 的流、緩沖區和管道等結構,并了解它們分別如何支持編寫內存有效的應用程序。

 [[312999]]

一座被設計為能避開氣流的建筑 (https://pixelz.cc)

軟件應用程序在計算機的主存儲器中運行,我們稱之為隨機存取存儲器(RAM)。JavaScript,尤其是 NodeJS (服務端 JS)允許我們為終端用戶編寫從小型到大型的軟件項目。處理程序的內存總是一個棘手的問題,因為糟糕的實現可能會阻塞在給定服務器或系統上運行的所有其他應用程序。C 和 C++ 程序員確實關心內存管理,因為隱藏在代碼的每個角落都有可能出現可怕的內存泄漏。但是對于 JS 開發者來說,你真的有關心過這個問題嗎?

由于 JS 開發人員通常在專用的高容量服務器上進行 web 服務器編程,他們可能不會察覺多任務處理的延遲。比方說在開發 web 服務器的情況下,我們也會運行多個應用程序,如數據庫服務器( MySQL )、緩存服務器( Redis )和其他需要的應用。我們需要知道它們也會消耗可用的主內存。如果我們隨意地編寫應用程序,很可能會降低其他進程的性能,甚至讓內存完全拒絕對它們的分配。在本文中,我們通過解決一個問題來了解 NodeJS 的流、緩沖區和管道等結構,并了解它們分別如何支持編寫內存有效的應用程序。

我們使用 NodeJS v8.12.0 來運行這些程序,所有代碼示例都放在這里:

narenaryan/node-backpressure-internals

原文鏈接:Writing memory efficient software applications in Node.js 

問題:大文件復制

如果有人被要求用 NodeJS 寫一段文件復制的程序,那么他會迅速寫出下面這段代碼: 

  1. const fs = require('fs');  
  2. let fileName = process.argv[2];  
  3. let destPath = process.argv[3];  
  4. fs.readFile(fileName, (err, data) => {  
  5.     if (err) throw err;  
  6.     fs.writeFile(destPath || 'output', data, (err) => {  
  7.         if (err) throw err;  
  8.     });   
  9.     console.log('New file has been created!');  
  10. }); 

這段代碼簡單地根據輸入的文件名和路徑,在嘗試對文件讀取后把它寫入目標路徑,這對于小文件來說是不成問題的。

現在假設我們有一個大文件(大于4 GB)需要用這段程序來進行備份。就以我的一個達 7.4G 的超高清4K 電影為例子好了,我用上述的程序代碼把它從當前目錄復制到別的目錄。 

  1. $ node basic_copy.js cartoonMovie.mkv ~/Documents/bigMovie.mkv 

然后在 Ubuntu(Linux )系統下我得到了這段報錯: 

  1. /home/shobarani/Workspace/basic_copy.js:7  
  2.     if (err) throw err;  
  3.              ^  
  4. RangeError: File size is greater than possible Buffer: 0x7fffffff bytes  
  5.     at FSReqWrap.readFileAfterStat [as oncomplete] (fs.js:453:11) 

正如你看到的那樣,由于 NodeJS 最大只允許寫入 2GB 的數據到它的緩沖區,導致了錯誤發生在讀取文件的過程中。為了解決這個問題,當你在進行 I/O 密集操作的時候(復制、處理、壓縮等),最好考慮一下內存的情況。

NodeJS 中的 Streams 和 Buffers

為了解決上述問題,我們需要一個辦法把大文件切成許多文件塊,同時需要一個數據結構去存放這些文件塊。一個 buffer 就是用來存儲二進制數據的結構。接下來,我們需要一個讀寫文件塊的方法,而 Streams 則提供了這部分能力。

Buffers(緩沖區)

我們能夠利用 Buffer 對象輕松地創建一個 buffer。 

  1. let buffer = new Buffer(10); # 10 為 buffer 的體積  
  2. console.log(buffer); # prints <Buffer 00 00 00 00 00 00 00 00 00 00> 

在新版本的 NodeJS (>8)中,你也可以這樣寫。 

  1. let buffer = new Buffer.alloc(10);  
  2. console.log(buffer); # prints <Buffer 00 00 00 00 00 00 00 00 00 00> 

如果我們已經有了一些數據,比如數組或者別的數據集,我們可以為它們創建一個 buffer。 

  1. let name = 'Node JS DEV' 
  2. let buffer = Buffer.from(name);  
  3. console.log(buffer) # prints <Buffer 4e 6f 64 65 20 4a 53 20 44 45 5> 

Buffers 有一些如 buffer.toString() 和 buffer.toJSON() 之類的重要方法,能夠深入到其所存儲的數據當中去。

我們不會為了優化代碼而去直接創建原始 buffer。NodeJS 和 V8 引擎在處理 streams 和網絡 socket 的時候就已經在創建內部緩沖區(隊列)中實現了這一點。

Streams(流)

簡單來說,流就像 NodeJS 對象上的任意門。在計算機網絡中,入口是一個輸入動作,出口是一個輸出動作。我們接下來將繼續使用這些術語。

流的類型總共有四種:

  •  可讀流(用于讀取數據)
  •  可寫流(用于寫入數據)
  •  雙工流(同時可用于讀寫)
  •  轉換流(一種用于處理數據的自定義雙工流,如壓縮,檢查數據等)

下面這句話可以清晰地闡述為什么我們應該使用流。

Stream API (尤其是 stream.pipe() 方法)的一個重要目標是將數據緩沖限制在可接受的水平,這樣不同速度的源和目標就不會阻塞可用內存。

我們需要一些辦法去完成任務而不至于壓垮系統。這也是我們在文章開頭就已經提到過的。

上面的示意圖中我們有兩個類型的流,分別是可讀流和可寫流。.pipe() 方法是一個非常基本的方法,用于連接可讀流和可寫流。如果你不明白上面的示意圖,也沒關系,在看完我們的例子以后,你可以回到示意圖這里來,那個時候一切都會顯得理所當然。管道是一種引人注目的機制,下面我們用兩個例子來說明它。

解法1(簡單地使用流來復制文件)

讓我們設計一種解法來解決前文中大文件復制的問題。首先我們要創建兩個流,然后執行接下來的幾個步驟。

  1.  監聽來自可讀流的數據塊
  2.  把數據塊寫進可寫流
  3.  跟蹤文件復制的進度

我們把這段代碼命名為 streams_copy_basic.js 

  1. /*  
  2.     A file copy with streams and events - Author: Naren Arya  
  3. */  
  4. const stream = require('stream');  
  5. const fs = require('fs');  
  6. let fileName = process.argv[2];  
  7. let destPath = process.argv[3];  
  8. const readabale = fs.createReadStream(fileName);  
  9. const writeable = fs.createWriteStream(destPath || "output");  
  10. fs.stat(fileName, (err, stats) => {  
  11.     this.fileSize = stats.size;  
  12.     this.counter = 1 
  13.     this.fileArray = fileName.split('.');     
  14.     try {  
  15.         this.duplicate = destPath + "/" + this.fileArray[0] + '_Copy.' + this.fileArray[1];  
  16.     } catch(e) {  
  17.         console.exception('File name is invalid! please pass the proper one');  
  18.     }    
  19.     process.stdout.write(`File: ${this.duplicate} is being created:`);    
  20.     readabale.on('data', (chunk)=> {  
  21.         let percentageCopied = ((chunk.length * this.counter) / this.fileSize) * 100;  
  22.         process.stdout.clearLine();  // clear current text  
  23.         process.stdout.cursorTo(0);  
  24.         process.stdout.write(`${Math.round(percentageCopied)}%`);  
  25.         writeable.write(chunk);  
  26.         this.counter += 1;  
  27.     });    
  28.     readabale.on('end', (e) => {  
  29.         process.stdout.clearLine();  // clear current text  
  30.         process.stdout.cursorTo(0);  
  31.         process.stdout.write("Successfully finished the operation");  
  32.         return;  
  33.     });    
  34.     readabale.on('error', (e) => {  
  35.         console.log("Some error occured: ", e);  
  36.     });     
  37.     writeable.on('finish', () => { 
  38.         console.log("Successfully created the file copy!");  
  39.     });     
  40. }); 

 

在這段程序中,我們接收用戶傳入的兩個文件路徑(源文件和目標文件),然后創建了兩個流,用于把數據塊從可讀流運到可寫流。然后我們定義了一些變量去追蹤文件復制的進度,然后輸出到控制臺(此處為 console)。與此同時我們還訂閱了一些事件:

data:當一個數據塊被讀取時觸發

end:當一個數據塊被可讀流所讀取完的時候觸發

error:當讀取數據塊的時候出錯時觸發

運行這段程序,我們可以成功地完成一個大文件(此處為7.4 G)的復制任務。 

  1. $ time node streams_copy_basic.js cartoonMovie.mkv ~/Documents/4kdemo.mkv 

然而,當我們通過任務管理器觀察程序在運行過程中的內存狀況時,依舊有一個問題。

4.6GB?我們的程序在運行時所消耗的內存,在這里是講不通的,以及它很有可能會卡死其他的應用程序。

發生了什么?

如果你有仔細觀察上圖中的讀寫率,你會發現一些端倪。

Disk Read: 53.4 MiB/s

Disk Write: 14.8 MiB/s

這意味著生產者正在以更快的速度生產,而消費者無法跟上這個速度。計算機為了保存讀取的數據塊,將多余的數據存儲到機器的RAM中。這就是RAM出現峰值的原因。

上述代碼在我的機器上運行了3分16秒…… 

  1. 17.16s user 25.06s system 21% cpu 3:16.61 total 

解法2(基于流和自動背壓的文件復制)

為了克服上述問題,我們可以修改程序來自動調整磁盤的讀寫速度。這個機制就是背壓。我們不需要做太多,只需將可讀流導入可寫流即可,NodeJS 會負責背壓的工作。

讓我們將這個程序命名為 streams_copy_efficient.js 

  1. /*  
  2.     A file copy with streams and piping - Author: Naren Arya  
  3. */  
  4. const stream = require('stream');  
  5. const fs = require('fs');  
  6. let fileName = process.argv[2];  
  7. let destPath = process.argv[3];  
  8. const readabale = fs.createReadStream(fileName);  
  9. const writeable = fs.createWriteStream(destPath || "output");  
  10. fs.stat(fileName, (err, stats) => {  
  11.     this.fileSize = stats.size;  
  12.     this.counter = 1 
  13.     this.fileArray = fileName.split('.');   
  14.     try {  
  15.         this.duplicate = destPath + "/" + this.fileArray[0] + '_Copy.' + this.fileArray[1];  
  16.     } catch(e) {  
  17.         console.exception('File name is invalid! please pass the proper one');  
  18.     }     
  19.     process.stdout.write(`File: ${this.duplicate} is being created:`);   
  20.     readabale.on('data', (chunk) => {  
  21.         let percentageCopied = ((chunk.length * this.counter) / this.fileSize) * 100;  
  22.         process.stdout.clearLine();  // clear current text  
  23.         process.stdout.cursorTo(0);  
  24.         process.stdout.write(`${Math.round(percentageCopied)}%`);  
  25.         this.counter += 1;  
  26.     });     
  27.     readabale.pipe(writeable); // Auto pilot ON!     
  28.     // In case if we have an interruption while copying  
  29.     writeable.on('unpipe', (e) => {  
  30.         process.stdout.write("Copy has failed!");  
  31.     });     
  32. }); 

在這個例子中,我們用一句代碼替換了之前的數據塊寫入操作。 

  1. readabale.pipe(writeable); // Auto pilot ON! 

這里的 pipe 就是所有魔法發生的原因。它控制了磁盤讀寫的速度以至于不會阻塞內存(RAM)。

運行一下。 

  1. $ time node streams_copy_efficient.js cartoonMovie.mkv ~/Documents/4kdemo.mkv 

我們復制了同一個大文件(7.4 GB),讓我們來看看內存利用率。

震驚!現在 Node 程序僅僅占用了61.9 MiB 的內存。如果你觀察到讀寫速率的話:

Disk Read: 35.5 MiB/s

Disk Write: 35.5 MiB/s

在任意給定的時間內,因為背壓的存在,讀寫速率得以保持一致。更讓人驚喜的是,這段優化后的程序代碼整整比之前的快了13秒。 

  1. 12.13s user 28.50s system 22% cpu 3:03.35 total 

由于 NodeJS 流和管道,內存負載減少了98.68%,執行時間也減少了。這就是為什么管道是一個強大的存在。

61.9 MiB 是由可讀流創建的緩沖區大小。我們還可以使用可讀流上的 read 方法為緩沖塊分配自定義大小。 

  1. const readabale = fs.createReadStream(fileName);  
  2. readable.read(no_of_bytes_size); 

除了本地文件的復制以外,這個技術還可以用于優化許多 I/O 操作的問題:

  •  處理從卡夫卡到數據庫的數據流
  •  處理來自文件系統的數據流,動態壓縮并寫入磁盤
  •  更多……

源碼(Git)

你可以在我的倉庫底下找到所有的例子并在自己的機器上測試。

narenaryan/node-backpressure-internals

結論

我寫這篇文章的動機,主要是為了說明即使 NodeJS 提供了很好的 API,我們也可能會一不留神就寫出性能很差的代碼。如果我們能更多地關注其內置的工具,我們便可以更好地優化程序的運行方式。

你在此可以找到更多關于“背壓”的資料:

backpressuring-in-streams

完。 

 

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2013-05-17 09:41:02

Node.js云應用開發IaaS

2023-03-07 14:31:44

Node.jsPython應用程序

2022-09-12 16:02:32

Docker安全Node.js

2020-09-04 15:06:04

Docker容器化Node.js

2022-12-14 14:40:27

Node.js開發應用程序

2022-05-09 17:33:23

PWA漸進式Web應用程序離線優先

2024-03-22 11:40:40

Node.jsNodeCRUD

2023-06-16 15:14:57

Node.js容器化開發

2023-03-24 15:57:31

Node.js應用程序容器

2012-09-17 11:26:14

IBMdw

2023-10-26 01:28:02

2014-07-11 14:16:15

AbsurdJSExpress

2025-08-11 07:58:55

2020-10-26 08:34:13

Node.jsCORS前端

2020-09-22 07:35:42

Node.jsVue.js文件壓縮

2015-11-20 17:09:36

jsWeb應用程序

2024-03-27 11:18:02

2023-10-18 16:39:32

2017-03-20 13:43:51

Node.js內存泄漏

2017-03-19 16:40:28

漏洞Node.js內存泄漏
點贊
收藏

51CTO技術棧公眾號

欧美激情黑人| 成人交换视频| 91女人视频在线观看| 国产综合在线观看视频| 性欧美又大又长又硬| 亚洲成av人在线观看| 天堂社区在线视频| 国产一区在线精品| 欧美精品在线一区| 欧美日韩国产高清| 97在线视频免费播放| 久久精品国产亚洲精品2020| 久草在线免费福利| 日本一二三不卡| 黄色99视频| 99视频精品| 美女被啪啪一区二区| 欧美性xxx| 欧美亚洲一区二区三区四区| wwwwxxxx日韩| 国产不卡视频在线观看| 懂色av一区二区三区四区五区| 欧美a级在线| 国产一区精品视频| 色婷婷综合久久久久久| 欧美精品videossex性护士| 国产精品色婷婷在线观看| 国产亚洲精品日韩| av漫画网站在线观看| 亚洲人成亚洲人成在线观看图片| 国产视频1区2区3区| 尤物视频一区二区| 国产精品国产三级国产专区51| 国产视频一视频二| 成人av在线电影| 国产中文欧美精品| 宅男噜噜噜66国产日韩在线观看| 欧美亚洲一区在线| 欧美激情麻豆| 欧美精品久久久久久久自慰| 中文字幕亚洲综合久久菠萝蜜| 快播av资源| 欧美成va人片在线观看| 九色porny丨首页入口在线| 午夜成人免费电影| 黄色影院在线看| 亚洲视频在线免费观看| 一级毛片在线看| 日韩激情第一页| 美女呻吟一区| 欧美日韩另类综合| 国产色综合一区| 国产综合视频一区二区三区免费| 亚洲人成毛片在线播放| 一本一道久久综合狠狠老| 精品一区二区中文字幕| 色婷婷综合久久久久中文| 日本aⅴ中文| 亚洲精品网站在线播放gif| 91麻豆精品国产91久久久平台| 国产深夜男女无套内射| 日本sm残虐另类| 先锋av资源在线| 91国语精品自产拍在线观看性色| 免费在线观看成人| fc2在线中文字幕| 亚洲一区二区免费| 亚洲欧美视频在线观看| 一区二区电影免费观看| 久久人人爽人人爽爽久久| 欧美做受69| 在线观看成人免费| 51午夜精品国产| 欧美不卡高清| 青青免费在线视频| 97超碰国产精品女人人人爽| 天天综合色天天综合色h| 欧美成熟毛茸茸| 国产精品色噜噜| 久久国产精品视频在线观看| 国产精品一区一区三区| 久中文字幕一区| 日韩中文字幕不卡| 国产欧美日韩丝袜精品一区| 欧美gayvideo| 欧美二级三级| 国产精品亚洲视频| 国产粉嫩一区二区三区在线观看| 97国产精品免费视频| 日韩理论片网站| 开心激情综合| 中文字幕国产免费| 国产欧美日韩高清| 欧美日韩视频在线第一区| 忘忧草精品久久久久久久高清| 污污网站在线| 麻豆精品视频| 精品国内亚洲在观看18黄| 青青国产91久久久久久| aa免费在线观看| 在线观看av一区| 忘忧草精品久久久久久久高清| 人人妻人人澡人人爽欧美一区双| 日韩中文字幕欧美| 亚洲欧美电影一区二区| 99精品国产一区二区三区2021| 羞羞网www| 97在线免费公开视频| 国产一区二区高清视频| 国内自拍欧美激情| 色噜噜狠狠狠综合曰曰曰| 欧美日韩性视频| 国产九九在线观看| 任你弄在线视频免费观看| 色妞一区二区三区| 亚洲精品国产a| 亚洲欧美色图小说| 亚洲电影中文字幕在线观看| 日本va欧美va欧美va精品| 筱崎爱全乳无删减在线观看| www.日本在线视频| 91免费福利视频| 色播久久人人爽人人爽人人片视av| 99精品欧美一区二区蜜桃免费| 99国产精品免费视频观看| 国语对白精品一区二区| 99精品在线观看| 老汉色老汉首页av亚洲| 在线亚洲国产精品网站| 国产精品99一区二区| 国产精品高清一区二区| 亚洲人成亚洲精品| 在线看片线路1| 欧美人与动牲交xxxxbbbb| 日本精品福利视频| 欧美日韩免费观看一区| 日韩av高清在线看片| 国产黄视频网站| 欧美日韩中文不卡| 男人操女人逼免费视频| av在线不卡一区| 538国产精品一区二区在线 | 一区二区不卡在线观看| 欧美日韩综合网| 9色porny| 97中文字幕在线| 一区二区不卡在线| 亚洲成人精品电影在线观看| 亚欧无线一线二线三线区别| 三年中文高清在线观看第6集| 亚洲天堂免费视频| 精品国产免费人成电影在线观...| 国产精品一区二区三区观看| 中文字幕日韩av综合精品| 日韩精品一二三四| 色呦呦呦在线观看| 中文网丁香综合网| 中文字幕九色91在线| 91免费观看国产| 日韩精品免费一区二区夜夜嗨 | 欧美一级日韩免费不卡| 日韩一区欧美二区| 色豆豆成人网| 99热在线免费播放| 99热国产免费| 亚洲国产欧美久久| 中文字幕欧美日韩一区| 国产精品久久久久无码av| 2024最新电影免费在线观看| 日韩a级在线观看| 国产成人拍精品视频午夜网站 | 国产一级大片| 久久这里精品国产99丫e6| 亚洲成人免费在线视频| 久久九九99视频| 91精品蜜臀一区二区三区在线| 亚洲综合伊人久久大杳蕉| www.爱色av.com| 91老司机在线| 亚洲免费一在线| 夜夜嗨av一区二区三区四季av| 亚洲欧美亚洲| 精品欧美日韩精品| 性感美女激情视频在线观看| 咪咪色在线视频| 国产精品精品视频| 亚洲精品一区二区三区影院| 国产精品久久久久久久久免费相片| 狠狠综合久久| 日韩色淫视频| 国家队第一季免费高清在线观看| 久久艹国产精品| 51国偷自产一区二区三区| 亚洲美女中文字幕| 欧美日韩久久久久| www.欧美日韩国产在线| 欧美私人啪啪vps| 国产精品45p| 性欧美18xxxhd| 三级国产在线观看|