由於Javascript本身設計就適合於單線程的應用, 但一般後端應用程式都會支援多個服務來處理client的請求, nodejs中也提供了
cluster模組來達成此功能。
Cluster的原理很簡單,由於每個Process都只能用單核心的CPU來運行,那麼就多開幾個來幫忙處理吧! 而這個Cluster的模組強大的地方在於讓我們只監聽一個Port,加上Master/Worker的概念,當收到請求後就分配給底下的Worker進行處理。
這樣有什麼好處? 遇到尖峰時刻時,我們可以妥善利用多核的優勢,避免有些CPU閒置造成浪費。
如何實做?
根據系統的cpu數量建立N個worker來進行處理。
// server.js
const cluster = require('cluster');
const cpuNums = require('os').cpus().length;
const http = require('http');
if (cluster.isMaster) {
console.info(`cpu: ${cpuNums}`);
for (var i = 0; i < cpuNums; i++) {
cluster.fork();
}
} else {
http.createServer(function(req, res) {
res.end(`response from worker ${process.pid}`);
}).listen(5000);
console.log(`Worker ${process.pid} started`);
}
建立測試腳本
for ((i = 1; i <= 8; i++)); do
curl <http://127.0.0.1:5000>
echo ""
done
輸出如下, 可以看到response來自不同的worker:
response from worker 7872
response from worker 7878
response from worker 7886
response from worker 7893
response from worker 7853
response from worker 7860
response from worker 7855
response from worker 7866
如何將請求分發到多個worker
每個worker建立時都會於master上註冊並建立IPC通道, 而客戶端請求到達時, master會負責將請求分配給worker。
這裡可能會有疑問是這些請求是如何被分配的?採取什麼策略?
預設的分配策略是輪詢的方式, 當請求到達時, master會輪詢一遍worker列表, 看誰有空閒就將請求分配給該worker進行處理。
另外也支援無分配策略但這種方式可能會造成搶食現象的競爭問題。
可以透過環境變數NODE_CLUSTER_SCHED_POLICY設定, 也可以在cluster.setupMaster(options)時傳入。
process之間的通訊
由於各個process無法共享資源, 但可以藉由IPC通訊方式讓master與worker之間進行通信, 基本用法如下:
// worker 發送訊息
process.send('讀取訊息');// master 接收消息 -> 處理 -> 回應
cluster.on('online', function (worker) {
// worker建立時,開始監聽message事件
cluster.workers[id].on('message', function(data) {
// 處理來自worker發送的資料
// 回傳給worker
cluster.workers[id].send('result')
});
});