淺談node中的cluster集群
結(jié)論
雖然平常通過設(shè)置為CPU進(jìn)程數(shù)的工作進(jìn)程,但是可以超過這個數(shù),并且并不是主進(jìn)程先創(chuàng)建
if (cluster.isMaster) { // 循環(huán) fork 任務(wù) CPU i5-7300HQ 四核四進(jìn)程 for (let i = 0; i < 6; i++) { cluster.fork() } console.log(chalk.green(`主進(jìn)程運行在${process.pid}`)) } else { app.listen(1314) // export app 一個 Koa 服務(wù)器的實例 console.log(chalk.green(`子進(jìn)程運行在${process.pid}`)) } #子進(jìn)程運行在17768 #子進(jìn)程運行在5784 #子進(jìn)程運行在11232 #子進(jìn)程運行在7904 #主進(jìn)程運行在12960 #子進(jìn)程運行在4300 #子進(jìn)程運行在16056
在主進(jìn)程中 cluster 表示主進(jìn)程(用于監(jiān)聽、發(fā)送事件), process 是本身的進(jìn)程,worker 表示子進(jìn)程,通過 cluster.workers 獲取
在子進(jìn)程中 process 表示子進(jìn)程(用于監(jiān)聽、發(fā)送事件),也可以通過 cluster.worker 表示當(dāng)前子進(jìn)程
cluster.worker.process 等價于 process(在子進(jìn)程中)
主進(jìn)程子進(jìn)程相互通信
- cluster 用于監(jiān)聽 process(child) 子進(jìn)程觸發(fā)的各種事件
- worker 在主進(jìn)程中獲取,用于和自身通信。當(dāng)子進(jìn)程觸發(fā)事件時,會返回當(dāng)前的 worker 以及相關(guān)的信息到主進(jìn)程相應(yīng)的事件中
- process(parent) 主進(jìn)程本身的進(jìn)程實例,在通信過程中基本沒有用到
- process(child) 子進(jìn)程本身的實例,只能在子進(jìn)程獲取用于監(jiān)聽自身的事件
可見主進(jìn)程與子進(jìn)程通過這樣一個三角關(guān)系互相通信,其中 cluster 和 worker 是在主進(jìn)程中獲取的,process(child) 是子進(jìn)程。 cluster 通過操作 worker 通知子進(jìn)程,子進(jìn)程本身和 cluster 進(jìn)行通信。為什么要這樣設(shè)計呢?因為子進(jìn)程會有多個,只有通過 worker 才能選擇和哪個進(jìn)程通信
子進(jìn)程的調(diào)度策略 cluster.schedulingPolicy
調(diào)度策略,包括循環(huán)計數(shù)的 cluster.SCHED_RR,以及由操作系統(tǒng)決定的cluster.SCHED_NONE。 這是一個全局設(shè)置,當(dāng)?shù)谝粋€工作進(jìn)程被衍生或者調(diào)動cluster.setupMaster()時,都將第一時間生效。除Windows外的所有操作系統(tǒng)中,SCHED_RR都是默認(rèn)設(shè)置。只要libuv可以有效地分發(fā)IOCP handle,而不會導(dǎo)致嚴(yán)重的性能沖擊的話,Windows系統(tǒng)也會更改為SCHED_RR。cluster.schedulingPolicy 可以通過設(shè)置NODE_CLUSTER_SCHED_POLICY環(huán)境變量來實現(xiàn)。這個環(huán)境變量的有效值包括"rr" 和 "none"。
RR 即 Round-Robin 輪詢調(diào)度,即每個子進(jìn)程的獲取的事件的機會是均等的,這是除 windows以外默認(rèn)的。而 windows 下的調(diào)度策略很詭異,見下圖。目前并沒有相關(guān) API 可以設(shè)置調(diào)度策略的算法,node 只為我們提供了兩個值
進(jìn)程調(diào)度算法.png
測試數(shù)據(jù)為 1000次 并發(fā)請求,重復(fù)測試20次,在windows下的表現(xiàn)情況??梢?windows 的調(diào)度算法表現(xiàn)的雜亂無章。如果是 RR 算法四條進(jìn)程的調(diào)度應(yīng)該處于同一橫線上。暫時沒在本地搭建 linux 環(huán)境,有條件的同學(xué)可以協(xié)助測試一波。
cluster的調(diào)度算法目前至于系統(tǒng)有關(guān)
多進(jìn)程間的鑒權(quán)問題
注意:Node.js不支持路由邏輯。因此在設(shè)計應(yīng)用時,不應(yīng)該過分依賴內(nèi)存數(shù)據(jù)對象(如sessions和login等)。由于各工作進(jìn)程是獨立的進(jìn)程,它們可以根據(jù)需要隨時關(guān)閉或重新生成,而不影響其他進(jìn)程的正常運行。只要有存活的工作進(jìn)程,服務(wù)器就可以繼續(xù)處理連接。如果沒有存活的工作進(jìn)程,現(xiàn)有連接會丟失,新的連接也會被拒絕。Node.js不會自動管理工作進(jìn)程的數(shù)量,而應(yīng)該由具體的應(yīng)用根據(jù)實際需要來管理進(jìn)程池。
文檔中已明確說明了,每一個工作進(jìn)程都是獨立的,并且互相之間除了能夠進(jìn)行通信外,沒有辦法共享內(nèi)存。所以在設(shè)計鑒權(quán)的時候,有兩種方法
- 通過共有的主進(jìn)程存儲鑒權(quán)信息,每次前端提交帳號密碼,授權(quán)完成后,將 token 發(fā)送給主進(jìn)程,下次前臺查詢時先在主進(jìn)程獲取授權(quán)信息
- 通過統(tǒng)一的外部 redis 存取
兩種方法看來還是第二種好的不要太多,因此多進(jìn)程的環(huán)境下,應(yīng)該使用外部數(shù)據(jù)庫統(tǒng)一存儲 token 信息
進(jìn)一步的子進(jìn)程間通信思考
雖然 node 中并沒有直接提供的進(jìn)程間通訊功能,但是我們可以通過主進(jìn)程相互協(xié)調(diào)進(jìn)程間的通訊功能,需要定義標(biāo)準(zhǔn)的通信格式,例如
interface cmd { type: string from: number to: number msg: any }
這樣通過統(tǒng)一的格式,主進(jìn)程就可以識別來自各個進(jìn)程間的通信,起到進(jìn)程通信中樞的功能
egg.js 中 agent 的實現(xiàn)
+--------+ +-------+ | Master |<-------->| Agent | +--------+ +-------+ ^ ^ ^ / | \ / | \ / | \ v v v +----------+ +----------+ +----------+ | Worker 1 | | Worker 2 | | Worker 3 | +----------+ +----------+ +----------+
我們看到 egg 在多進(jìn)程模型之間實現(xiàn)了一個 agent 進(jìn)程,這個進(jìn)程主要負(fù)責(zé)對整個系統(tǒng)的定期維護(hù)
說到這里,Node.js 多進(jìn)程方案貌似已經(jīng)成型,這也是我們早期線上使用的方案。但后來我們發(fā)現(xiàn)有些工作其實不需要每個 Worker 都去做,如果都做,一來是浪費資源,更重要的是可能會導(dǎo)致多進(jìn)程間資源訪問沖突。舉個例子:生產(chǎn)環(huán)境的日志文件我們一般會按照日期進(jìn)行歸檔,在單進(jìn)程模型下這再簡單不過了:
每天凌晨 0 點,將當(dāng)前日志文件按照日期進(jìn)行重命名
銷毀以前的文件句柄,并創(chuàng)建新的日志文件繼續(xù)寫入
試想如果現(xiàn)在是 4 個進(jìn)程來做同樣的事情,是不是就亂套了。所以,對于這一類后臺運行的邏輯,我們希望將它們放到一個單獨的進(jìn)程上去執(zhí)行,這個進(jìn)程就叫 Agent Worker,簡稱 Agent。Agent 好比是 Master 給其他 Worker 請的一個『秘書』,它不對外提供服務(wù),只給 App Worker 打工,專門處理一些公共事務(wù)。
這樣我們可以指定一個進(jìn)程作為 agent 進(jìn)程,用于實現(xiàn)自己定義的事務(wù)。在 egg 中,主線程啟動后 首先 fork agent進(jìn)程,當(dāng) agent 進(jìn)程啟動完成后再啟動具體的 worker 進(jìn)程。參照上面的代碼,相信這部分邏輯現(xiàn)在也不難實現(xiàn)了。這樣 agent 就會獲得 id 為1的進(jìn)程
最后
P.S 勘誤,圖2中的線程應(yīng)該為進(jìn)程,獨立的nodejs進(jìn)程
本文相關(guān)代碼在 github
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
一次NodeJS內(nèi)存泄漏排查的實戰(zhàn)記錄
這篇文章主要給大家介紹了一次NodeJS內(nèi)存泄漏排查的實戰(zhàn)記錄,文中給出了詳細(xì)的排查過程以及內(nèi)存泄漏的解決方法,大家可以學(xué)習(xí)一下以備不時之需,需要的朋友可以參考下2022-03-03node.js利用redis數(shù)據(jù)庫緩存數(shù)據(jù)的方法
Redis數(shù)據(jù)庫采用極簡的設(shè)計思想,最新版的源碼包還不到2Mb。其在使用上也有別于一般的數(shù)據(jù)庫。下面這篇文章就來給大家介紹了node.js利用redis數(shù)據(jù)庫緩存數(shù)據(jù)的方法,需要的朋友可以參考借鑒,下面來一起看看吧。2017-03-03Node.js中的HTTP?Server對象與GET、POST請求
這篇文章介紹了Node.js中的HTTP?Server對象與GET、POST請求,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07用node開發(fā)并發(fā)布一個cli工具的方法步驟
這篇文章主要介紹了用node開發(fā)并發(fā)布一個cli工具的方法步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01