如何理解node的多進程

事件驅(qū)動

nodejs基于chrome v8引擎構(gòu)建,默認單線程單進程模式,nodejs的單線程指的是js引擎只有一個實例,且是在主線程執(zhí)行的(減少線程間切換的開銷,不用考慮鎖和線程池問題),其他的異步IO和事件驅(qū)動相關(guān)的線程通過libuv實現(xiàn)內(nèi)部的線程池和線程調(diào)度。libuv存在一個事件循環(huán),事件循環(huán)中維持一個執(zhí)行棧和任務(wù)隊列,在執(zhí)行棧中,如果有異步IO及定時器等函數(shù)的話,就把異步回調(diào)函數(shù)放入事件隊列中,執(zhí)行棧執(zhí)行完成后,從事件隊列中按照一定的順序執(zhí)行事件隊列中的異步回調(diào)。

模塊調(diào)用

nodejs中的模塊調(diào)用,js代碼調(diào)用c++核心模塊,核心模塊調(diào)用內(nèi)建模塊,通過libuv進行系統(tǒng)調(diào)用,送入線程池等待執(zhí)行。線程池中的I/O操作調(diào)用完成之后會保存結(jié)果向IOCP(一種高性能的I/O模型,一種應(yīng)用程序使用線程池處理異步I/O請求的機制)提交執(zhí)行狀態(tài)告知當前對象操作完成并將線程歸還線程池

非阻塞I/O

程序執(zhí)行過程中需要進行很多I/O操作,讀寫文件、輸入輸出、請求響應(yīng)等,I/O操作較為費時,基于node的事件循環(huán)機制,在I/O操作的同時,可以繼續(xù)執(zhí)行其他的代碼。如文件IO,nodejs調(diào)用libuv后,libuv在其他線程執(zhí)行I/O任務(wù),線程完成任務(wù)后會通知主線程,主線程回調(diào)nodejs。

多進程

nodejs以單線程模式運行,使用事件驅(qū)動處理并發(fā),可以在多核CPU系統(tǒng)上創(chuàng)建多個子線程。進程分為master進程和worker進程,master進程負責調(diào)度和管理worker進程,worker進程負責具體的業(yè)務(wù)處理,在服務(wù)器層面,worker是一個服務(wù)進程,負責處理來自客戶端的請求,多個worker相當于多個服務(wù)器,因此構(gòu)成一個服務(wù)器群,master進程負責創(chuàng)建worker,接收客戶端的請求,分配到各服務(wù)器上去處理,監(jiān)控worker的運行狀態(tài)及管理操作。

node提供child_process模塊創(chuàng)建子進程。nodejs實現(xiàn)多進程

  • spawn 使用指定的命令行參數(shù)創(chuàng)建新進程
  • fork 用于在子進程中運行的模塊,基于spawn的封裝,fork('./main.js)相當于spawn('node', ['./main.js']),fork會在父進程與子進程之間,建立一個用于通信的管道,用于進程間的通信
  • exec 使用子進程執(zhí)行命令,緩存子進程的輸出,將子進程的輸出以回調(diào)函數(shù)參數(shù)的形式返回。execFile基于spawn封裝,可以直接創(chuàng)建子進程進行文件操作,exec基于execFile封裝,可以直接開啟子進程執(zhí)行命令,常見的應(yīng)用場景如http-server以及webpack-dev-server命令行在啟動本地服務(wù)是自動打開瀏覽器。

淺談NodeJS多進程服務(wù)架構(gòu)基本原理

集群與進程通信

node主進程在創(chuàng)建子進程的時候吧把主進程正在監(jiān)聽的server當作參數(shù)傳給子進程,子進程自己也創(chuàng)建一個server來監(jiān)聽主進程傳來的server。通過process.on中的message接受信息,使用process.send發(fā)送信息。

  • ipc標準進程通信使用send方法發(fā)送消息時,第二個參數(shù)支持傳入一個服務(wù),必須是http服務(wù)或tcp服務(wù),子進程通過message事件進行接收,回調(diào)的參數(shù)對應(yīng)發(fā)送的參數(shù),第一個參數(shù)為消息,第二個參數(shù)為服務(wù),可以在子進程創(chuàng)建服務(wù)并對主進程的服務(wù)進行監(jiān)聽和操作
const os = require("os"); // os 模塊用于獲取系統(tǒng)信息
const http = require("http");
const path = require("path");
const { fork } = rquire("child_process");

// 創(chuàng)建服務(wù)
const server = createServer((res, req) => {
    res.end("hello");
}).listen(3000);

// 根據(jù) CPU 個數(shù)創(chuàng)建子進程
os.cpus().forEach(() => {
    fork("child_server.js", {
        cwd: path.join(__dirname);
    }).send("server", server);
});

/**-------------------子進程業(yè)務(wù)的代碼----------------------*/
const http = require("http");
// 接收來自主進程發(fā)來的服務(wù)
process.on("message", (data, server) => {
    http.createServer((req, res) => {
        res.end(`child${process.pid}`);
    }).listen(server); // 子進程共用主進程的服務(wù)
});
  • 單個nodejs實例運行在單個線程中,為了充分利用多核系統(tǒng),有時需要啟用一組nodejs進程去處理負載任務(wù),cluster模塊可以創(chuàng)建共享服務(wù)器端口的子進程。cluster模塊,通過isMaster屬性,判斷是否是Master進程,是則fork子進程,否則啟動一個server,每個HTTP Server都能監(jiān)聽到同一個端口。cluster

cluster 模塊可以創(chuàng)建共享服務(wù)器端口的子進程。

const cluster = require("cluster");
const http = require("http");
const os = require("os");

// 判斷當前執(zhí)行的進程是否為主進程,為主進程則創(chuàng)建子進程,否則用子進程監(jiān)聽服務(wù)
if (cluster.isMaster) {
    // 創(chuàng)建子進程
    os.cpus().forEach(() => cluster.fork());
} else {
    // 創(chuàng)建并監(jiān)聽服務(wù)
    http.createServer((req, res) => {
        res.end(`child${process.pid}`);
    }).listen(3000);
}

模塊分類

  • 核心模塊:包含在nodejs源碼中,被編譯進nodejs可執(zhí)行二進制文件的js模塊,是lib和deps目錄下的js文件,如http/fs等
  • 內(nèi)建模塊: 一般不直接調(diào)用,在native模塊中調(diào)用,再require
  • 第三方模塊:非nodejs源碼自帶的模塊為第三方模塊,如webpack/express,如lib目錄下的fs.jsnative模塊,而fs.js調(diào)用的src目錄下的node_fs.cc是內(nèi)建模塊

nodejs如何和libuv和v8一起合作

nodejs深入系列文章

Node.js運行原理、高并發(fā)性能測試對比及生態(tài)圈匯總

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容