前言
最近在研究如何讓 Node.js 的程式可以同時接受大量的 Request ,
因為 Node.js 是單執行緖,想要達到目的,大約幾種方式,
1.) 增加 Thread 的數量
2.) 增加 Process 的數量
3.) 減少 Request 處理時間
減少 Request 處理時間可透過重構,調整程式 Logic 等方式,
接下來就 增加 Thread or Process 的方式及適用情況來說明
以下依 How To Use Multithreading in Node.js 及 How, in general, does Node.js handle 10,000 concurrent requests? 內容來分享
測試
1.) 增加 Thread 的數量
來自 How To Use Multithreading in Node.js 說明,
一個 Node.js Process 包含 7 條 Thread,1 條 Main Thread, 4 條 Node.js Thread 及 2 條 V8 Thread
4 條 Node.js Thread 就是來幫忙處理 I/O 操作,讓它不會 Block 住 main thread,
所以針對 I/O 操作幾乎不需要自已建立 Thread 來處理,有時自已建立 Thread 來處理 I/O 操作還可能會有效能問題。
那什麼狀況下,需要增加 Thread 來處理呢?
如果是 CPU-bound 的工作,就可以透過增加的 Thread 來處理,以避免 Block 住 Main Thread
以下建立程式來試試看,
1 | mkdir multi-threading |
新增 index.js 來測試 CPU-bound 工作會 Block 住 Main thread
1 | const express = require("express"); |
程式中,在 const counter = await calculateCounter()
已使用了 async/await ,
它還是會 Block 住其他的 Request ,
執行 node index.js
使用 Browser 連到 http://localhost:3000/blocking
後,
開啟另一個 Browser 連到 http://localhost:3000/non-blocking
,
該網頁要等 /blocking
處理完後,才會呈現出來。
所以 CPU-bound 的工作,使用 async/await 並無法讓它 non-blocking ,
這時就可以透過 Node.js 的 worker-threads
模組來將 CPU-bound 的工作放到額外的 Thread 中執行,讓它變成 non-blocking
因為建立Worker
要傳入 js 檔案,所以新增worker.js
,裡面是 CPU-bound 的工作
1 | const { parentPort } = require("worker_threads"); |
將執行後的結果,透過parentPort.postMessage
傳回給 Parent Thread,
新增 index2.js 透過 Worker
來建立額外的 Thread ,
並透過 worker.on('Event', fun)
來取得資料
1 | const express = require("express"); |
執行 node index2.js
使用 Browser 連到 http://localhost:3000/blocking
後,
開啟另一個 Browser 連到 http://localhost:3000/non-blocking
,
該網頁會馬上呈現,並不會被 /blocking
Block 住。
那如果一個 CPU-bound 的工作執行太久,則可以想辦法將它們拆分成不同個工作,
然後建立多個 Worker
來處理,
就像 How To Use Multithreading in Node.js 最後的範例,
建立 4 個 Worker 並將回傳的 Promise 放到 Array 之中,最後透過 Promise.all
等它們執行完畢。
2.) 增加 Process 的數量
如果已將 CPU-bound 工作放到額外的 Thread 執行,效能還是有問題,
則可以透過 Cluster
來增加 Process 處理大量的 Request
建立 index4.js ,在 /blocking
中還是執行 CPU-bound 工作,
最後透過 cluster.fork
建立多個 Process,
1 | const express = require("express"); |
執行 node index4.js
如果開啟 Browser 連到 http://localhost:3000/blocking
或是 http://localhost:3000/non-blocking
,
可以發現有時會被 Block 住,有時又不會,
當執行在同一個 Process 中,就會被 Block,不同就不會 Block 到。
另外要注意的是,因為是不同的 Process ,所以是不同的 Memory ,
可以發現 globalVar
及 globalVarB
在不同的 Process 是不會影響到的。
3.) iisNode 的設定
iisNode 並不 Support Cluster ,如果要多個 Node.js Process ,
則可以透過設定 nodeProcessCountPerApplication
的值(預設是 1)
詳細可以參考 iisnode.yml
參考資源
How To Use Multithreading in Node.js
How, in general, does Node.js handle 10,000 concurrent requests?
Optimizing your Node.js app’s performance with clustering