淺談node中的cluster集群
結論
雖然平常通過設置為CPU進程數的工作進程,但是可以超過這個數,并且并不是主進程先創(chuàng)建
if (cluster.isMaster) {
// 循環(huán) fork 任務 CPU i5-7300HQ 四核四進程
for (let i = 0; i < 6; i++) {
cluster.fork()
}
console.log(chalk.green(`主進程運行在${process.pid}`))
} else {
app.listen(1314) // export app 一個 Koa 服務器的實例
console.log(chalk.green(`子進程運行在${process.pid}`))
}
#子進程運行在17768
#子進程運行在5784
#子進程運行在11232
#子進程運行在7904
#主進程運行在12960
#子進程運行在4300
#子進程運行在16056
在主進程中 cluster 表示主進程(用于監(jiān)聽、發(fā)送事件), process 是本身的進程,worker 表示子進程,通過 cluster.workers 獲取
在子進程中 process 表示子進程(用于監(jiān)聽、發(fā)送事件),也可以通過 cluster.worker 表示當前子進程
cluster.worker.process 等價于 process(在子進程中)
主進程子進程相互通信

- cluster 用于監(jiān)聽 process(child) 子進程觸發(fā)的各種事件
- worker 在主進程中獲取,用于和自身通信。當子進程觸發(fā)事件時,會返回當前的 worker 以及相關的信息到主進程相應的事件中
- process(parent) 主進程本身的進程實例,在通信過程中基本沒有用到
- process(child) 子進程本身的實例,只能在子進程獲取用于監(jiān)聽自身的事件
可見主進程與子進程通過這樣一個三角關系互相通信,其中 cluster 和 worker 是在主進程中獲取的,process(child) 是子進程。 cluster 通過操作 worker 通知子進程,子進程本身和 cluster 進行通信。為什么要這樣設計呢?因為子進程會有多個,只有通過 worker 才能選擇和哪個進程通信
子進程的調度策略 cluster.schedulingPolicy
調度策略,包括循環(huán)計數的 cluster.SCHED_RR,以及由操作系統(tǒng)決定的cluster.SCHED_NONE。 這是一個全局設置,當第一個工作進程被衍生或者調動cluster.setupMaster()時,都將第一時間生效。除Windows外的所有操作系統(tǒng)中,SCHED_RR都是默認設置。只要libuv可以有效地分發(fā)IOCP handle,而不會導致嚴重的性能沖擊的話,Windows系統(tǒng)也會更改為SCHED_RR。cluster.schedulingPolicy 可以通過設置NODE_CLUSTER_SCHED_POLICY環(huán)境變量來實現。這個環(huán)境變量的有效值包括"rr" 和 "none"。
RR 即 Round-Robin 輪詢調度,即每個子進程的獲取的事件的機會是均等的,這是除 windows以外默認的。而 windows 下的調度策略很詭異,見下圖。目前并沒有相關 API 可以設置調度策略的算法,node 只為我們提供了兩個值

進程調度算法.png
測試數據為 1000次 并發(fā)請求,重復測試20次,在windows下的表現情況??梢?windows 的調度算法表現的雜亂無章。如果是 RR 算法四條進程的調度應該處于同一橫線上。暫時沒在本地搭建 linux 環(huán)境,有條件的同學可以協(xié)助測試一波。
cluster的調度算法目前至于系統(tǒng)有關
多進程間的鑒權問題
注意:Node.js不支持路由邏輯。因此在設計應用時,不應該過分依賴內存數據對象(如sessions和login等)。由于各工作進程是獨立的進程,它們可以根據需要隨時關閉或重新生成,而不影響其他進程的正常運行。只要有存活的工作進程,服務器就可以繼續(xù)處理連接。如果沒有存活的工作進程,現有連接會丟失,新的連接也會被拒絕。Node.js不會自動管理工作進程的數量,而應該由具體的應用根據實際需要來管理進程池。
文檔中已明確說明了,每一個工作進程都是獨立的,并且互相之間除了能夠進行通信外,沒有辦法共享內存。所以在設計鑒權的時候,有兩種方法
- 通過共有的主進程存儲鑒權信息,每次前端提交帳號密碼,授權完成后,將 token 發(fā)送給主進程,下次前臺查詢時先在主進程獲取授權信息
- 通過統(tǒng)一的外部 redis 存取
兩種方法看來還是第二種好的不要太多,因此多進程的環(huán)境下,應該使用外部數據庫統(tǒng)一存儲 token 信息
進一步的子進程間通信思考
雖然 node 中并沒有直接提供的進程間通訊功能,但是我們可以通過主進程相互協(xié)調進程間的通訊功能,需要定義標準的通信格式,例如
interface cmd {
type: string
from: number
to: number
msg: any
}
這樣通過統(tǒng)一的格式,主進程就可以識別來自各個進程間的通信,起到進程通信中樞的功能
egg.js 中 agent 的實現
+--------+ +-------+
| Master |<-------->| Agent |
+--------+ +-------+
^ ^ ^
/ | \
/ | \
/ | \
v v v
+----------+ +----------+ +----------+
| Worker 1 | | Worker 2 | | Worker 3 |
+----------+ +----------+ +----------+
我們看到 egg 在多進程模型之間實現了一個 agent 進程,這個進程主要負責對整個系統(tǒng)的定期維護
說到這里,Node.js 多進程方案貌似已經成型,這也是我們早期線上使用的方案。但后來我們發(fā)現有些工作其實不需要每個 Worker 都去做,如果都做,一來是浪費資源,更重要的是可能會導致多進程間資源訪問沖突。舉個例子:生產環(huán)境的日志文件我們一般會按照日期進行歸檔,在單進程模型下這再簡單不過了:
每天凌晨 0 點,將當前日志文件按照日期進行重命名
銷毀以前的文件句柄,并創(chuàng)建新的日志文件繼續(xù)寫入
試想如果現在是 4 個進程來做同樣的事情,是不是就亂套了。所以,對于這一類后臺運行的邏輯,我們希望將它們放到一個單獨的進程上去執(zhí)行,這個進程就叫 Agent Worker,簡稱 Agent。Agent 好比是 Master 給其他 Worker 請的一個『秘書』,它不對外提供服務,只給 App Worker 打工,專門處理一些公共事務。
這樣我們可以指定一個進程作為 agent 進程,用于實現自己定義的事務。在 egg 中,主線程啟動后 首先 fork agent進程,當 agent 進程啟動完成后再啟動具體的 worker 進程。參照上面的代碼,相信這部分邏輯現在也不難實現了。這樣 agent 就會獲得 id 為1的進程
最后
P.S 勘誤,圖2中的線程應該為進程,獨立的nodejs進程
本文相關代碼在 github
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
基于nodejs使用express創(chuàng)建web服務器的操作步驟
express實際上是對nodejs內置http進行封裝后的第三方包,其中提供了快捷創(chuàng)建web服務器以及處理請求路由的方法,使我們可以更加方便快捷的實現一個web服務器項目,本文件給大家詳細介紹基于nodejs使用express?創(chuàng)建web服務器的操作步驟2023-07-07
Node.js中的文件系統(tǒng)(file system)模塊詳解
Node.js文件系統(tǒng)模塊提供了豐富的方法,用于讀取、寫入、操作文件和目錄,文件系統(tǒng)模塊是Node.js強大而靈活的一部分,為文件操作提供了方便的API,本文給大家介紹Node.js中的文件系統(tǒng)(file system)模塊,感興趣的朋友一起看看吧2023-11-11

