詳解如何利用Nodejs構(gòu)建多進(jìn)程應(yīng)用
前言
JavaScript 主線(xiàn)程運(yùn)行在單個(gè)進(jìn)程的單個(gè)線(xiàn)程上。這樣做的好處是:
- 程序狀態(tài)是單一的,在沒(méi)有多線(xiàn)程的情況下沒(méi)有鎖、線(xiàn)程同步問(wèn)題,
- 操作系統(tǒng)在調(diào)度時(shí)因?yàn)檩^少上下文的切換,可以很好地提高CPU的使用率。
但是單進(jìn)程單線(xiàn)程并非完美的結(jié)構(gòu),一旦線(xiàn)程中某段代碼發(fā)生異常阻塞,會(huì)阻塞代碼執(zhí)行而浪費(fèi)資源。同時(shí),如今CPU基本均是多核的,服務(wù)器往往還有多個(gè)CPU。
因此 Nodejs 不可避免的面臨兩個(gè)問(wèn)題:如何充分利用多核CPU
****和 ****如何保證進(jìn)程的健壯性和穩(wěn)定性
。 另外,由于 Nodejs 中所有處理都在單線(xiàn)程上進(jìn)行,影響事件驅(qū)動(dòng)服務(wù)模型性能的點(diǎn)在于CPU的計(jì)算能力,而不受多進(jìn)程或多線(xiàn)程模式中資源上限的影響,可伸縮性遠(yuǎn)比前兩者高。如果解決掉多核CPU的利用問(wèn)題,帶來(lái)的性能上提升是可觀(guān)的。
進(jìn)程的創(chuàng)建和使用
多核利用率
面對(duì)單進(jìn)程單線(xiàn)程對(duì)多核使用不足的問(wèn)題,最簡(jiǎn)單的方法是啟動(dòng)多進(jìn)程即可。理想狀態(tài)下每個(gè)進(jìn)程各自利用一個(gè)CPU,以此實(shí)現(xiàn)多核CPU的利用。Nodejs 提供了 child_process模塊
,并且也提供了child_process.fork()
方法幫助我們實(shí)現(xiàn)進(jìn)程的復(fù)制。
你可以通過(guò)這個(gè)方法在本地啟動(dòng)多個(gè) HTTP 服務(wù),首先編寫(xiě)一段創(chuàng)建 http服務(wù)端
的代碼:
const http = require("http"); const server = http.createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("hello,world!"); }); // 隨機(jī)監(jiān)聽(tīng)1000-1999的任意一個(gè)端口 server.listen(Math.floor((1 + Math.random()) * 1000));
然后創(chuàng)建一個(gè)主進(jìn)程 master.js
來(lái)啟動(dòng)和管理他們:
const fork = require("child_process").fork; const cpus = require("os").cpus(); console.log(cpus.length); for (let i = 0; i < cpus.length; i++) { fork('./server.js'); }
?? 在 Linux 系統(tǒng)中,你可以通過(guò) ps aux | grep worker.js
來(lái)直接查看,在 Windows 中,你能通過(guò) netstat -a
然后查看 1000-1999 端口的進(jìn)程。
這樣的模式稱(chēng)為 Master-Worker模式
,又稱(chēng) 主從模式
。如圖,進(jìn)程分為兩種:主進(jìn)程和工作進(jìn)程。這是典型的分布式架構(gòu)中用于并行處理業(yè)務(wù)的模式,具備較好的可伸縮性和穩(wěn)定性:
- 主進(jìn)程不負(fù)責(zé)具體的業(yè)務(wù)處理,而是負(fù)責(zé)調(diào)度或管理工作進(jìn)程,它是趨向于穩(wěn)定的。
- 工作進(jìn)程負(fù)責(zé)具體的業(yè)務(wù)處理,趨于不穩(wěn)定。
?? fork() 能讓我們復(fù)制進(jìn)程使每個(gè)CPU內(nèi)核都使用上,但是依然要切記 fork() 進(jìn)程是昂貴的。因?yàn)镹ode通過(guò)事件驅(qū)動(dòng)和異步IO的方式很大的緩解了并發(fā)問(wèn)題,所以這里啟動(dòng)多個(gè)進(jìn)程只是為了充分將CPU資源利用起來(lái),而不是為了解決并發(fā)問(wèn)題。
創(chuàng)建子進(jìn)程
child_process模塊
給予 Node 可以隨意創(chuàng)建子進(jìn)程(child_process)的能力。它提供了4個(gè)方法用于創(chuàng)建子進(jìn)程:
spawn()
:?jiǎn)?dòng)一個(gè)子進(jìn)程來(lái)執(zhí)行命令。exec()
:?jiǎn)?dòng)一個(gè)子進(jìn)程來(lái)執(zhí)行命令。與spawn()不同的是其接口不同,它有一個(gè)回調(diào)函數(shù)獲知子進(jìn)程的狀況。execFile()
:?jiǎn)?dòng)一個(gè)子進(jìn)程來(lái)執(zhí)行可執(zhí)行文件。fork()
:與 spawn() 類(lèi)似,不同點(diǎn)在于它創(chuàng)建 Node 的子進(jìn)程只需指定要執(zhí)行的 JavaScript 文件模塊即可。
const cp = require("child_process"); cp.spawn("node", ["./server.js"]); cp.exec("node server.js", (err, stdout, stderr) => { // TODO }); cp.execFile("server.js", (err, stdout, stderr) => { // TODO }); cp.fork('./server.js');
??
execFile()
只能用于可以直接執(zhí)行的文件。在 Linux 中,你可以在文件開(kāi)頭加入#! /usr/bin/env node
進(jìn)程間通信 IPC
在 Master-Worker模式
中,要實(shí)現(xiàn)主進(jìn)程管理和調(diào)度工作進(jìn)程的功能,需要主進(jìn)程和工作進(jìn)程之間的通信。子進(jìn)程對(duì)象則由 send()
方法實(shí)現(xiàn)主進(jìn)程向子進(jìn)程發(fā)送數(shù)據(jù),message事件
實(shí)現(xiàn)收聽(tīng)子進(jìn)程發(fā)來(lái)的數(shù)據(jù):
// parent.js const cp = require("child_process"); const n = cp.fork("./child.js"); n.on('message', (msg) => { console.log('parent got msg: ', msg); }); n.send({ s: 'hello, world', }); // child.js process.on('message', (msg) => { console.log('child got msg', msg); }); process.send({ b: 'bar', });
?? HTML5提出了 WebWorker API 來(lái)創(chuàng)建工作線(xiàn)程,而主進(jìn)程和工作進(jìn)程之間通過(guò) onmessage()
和 postMessage()
進(jìn)行通信。具體參考MDN文檔。
通過(guò) fork()
或者其他API,創(chuàng)建子進(jìn)程之后,為了實(shí)現(xiàn)父子進(jìn)程之間的通信,父進(jìn)程與子進(jìn)程之間將會(huì)創(chuàng)建IPC通道(Inter-Process Communication,即進(jìn)程間通信) 。通過(guò)IPC通道,父子進(jìn)程之間才能通過(guò)message和send()傳遞消息。
父進(jìn)程在實(shí)際創(chuàng)建子進(jìn)程之前,會(huì)創(chuàng)建IPC通道并監(jiān)聽(tīng)它,然后才真正創(chuàng)建出子進(jìn)程,并通過(guò)環(huán)境變量(NODE_CHANNEL_FD)告訴子進(jìn)程這個(gè)IPC通道的文件描述符。子進(jìn)程在啟動(dòng)的過(guò)程中,根據(jù)文件描述符去連接這個(gè)已存在的IPC通道,從而完成父子進(jìn)程之間的連接。
?? 只有啟動(dòng)的子進(jìn)程是Node進(jìn)程時(shí),子進(jìn)程才會(huì)根據(jù)環(huán)境變量去連接IPC通道。對(duì)于其他類(lèi)型的子進(jìn)程則無(wú)法實(shí)現(xiàn)進(jìn)程間通信,除非其他進(jìn)程也按約定去連接這個(gè)已經(jīng)創(chuàng)建好的IPC通道
總結(jié)
今天我們討論了 Nodejs 在多核利用方法的問(wèn)題,然后介紹了創(chuàng)建子進(jìn)程、進(jìn)程間通信與IPC通道的內(nèi)容。通過(guò)這些基礎(chǔ)技術(shù),用 child_process模塊
在單機(jī)上搭建 Nodejs集群
是件相對(duì)容易的事情,讓 Nodejs 能夠在多核CPU的環(huán)境下充分利用計(jì)算資源。
以上就是詳解如何利用Nodejs構(gòu)建多進(jìn)程應(yīng)用的詳細(xì)內(nèi)容,更多關(guān)于Nodejs構(gòu)建多進(jìn)程應(yīng)用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vscode安裝教程以及配置node.js環(huán)境全過(guò)程
這篇文章主要給大家介紹了關(guān)于vscode安裝教程以及配置node.js環(huán)境的相關(guān)資料,VSCode是一款由微軟開(kāi)發(fā)的輕量級(jí)編輯器,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10node.js中的fs.renameSync方法使用說(shuō)明
這篇文章主要介紹了node.js中的fs.renameSync方法使用說(shuō)明,本文介紹了fs.renameSync的方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12nodejs通過(guò)phantomjs實(shí)現(xiàn)下載網(wǎng)頁(yè)
這篇文章主要介紹了nodejs通過(guò)phantomjs實(shí)現(xiàn)下載網(wǎng)頁(yè)的方法,有需要的小伙伴可以參考下。2015-05-05淺談在koa2中實(shí)現(xiàn)頁(yè)面渲染的全局?jǐn)?shù)據(jù)
本篇文章主要介紹了淺談在koa2中實(shí)現(xiàn)頁(yè)面渲染的全局?jǐn)?shù)據(jù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10Node.js如何使用Diffie-Hellman密鑰交換算法詳解
Deffie-Hellman(簡(jiǎn)稱(chēng) DH) 密鑰交換是最早的密鑰交換算法之一,它使得通信的雙方能在非安全的信道中安全的交換密鑰,用于加密后續(xù)的通信消息。下面這篇文章主要給大家介紹了關(guān)于Node.js如何使用DiffieHellman密鑰交換算法的相關(guān)資料,需要的朋友可以參考下。2017-09-09node.js [superAgent] 請(qǐng)求使用示例
這篇文章主要介紹了node.js [superAgent] 請(qǐng)求使用示例,分別給大家匯總了post請(qǐng)求、get請(qǐng)求、delete請(qǐng)求和put請(qǐng)求的示例,推薦給大家,希望大家能夠喜歡。2015-03-03