node進(jìn)程間通信

作為一名合格的程序猿/媛双藕,對(duì)于進(jìn)程蠢甲、線程還是有必要了解一點(diǎn)的呻澜,本文將從下面幾個(gè)方向進(jìn)行梳理,盡量做到知其然并知其所以然:

  • 進(jìn)程和線程的概念和關(guān)系
  • 進(jìn)程演進(jìn)
  • 進(jìn)程間通信
  • 理解底層基礎(chǔ)涂臣,助力上層應(yīng)用
  • 進(jìn)程保護(hù)

進(jìn)程和線程的概念和關(guān)系

用戶下達(dá)運(yùn)行程序的命令后盾计,就會(huì)產(chǎn)生進(jìn)程。同一程序可產(chǎn)生多個(gè)進(jìn)程(一對(duì)多關(guān)系)赁遗,以允許同時(shí)有多位用戶運(yùn)行同一程序闯估,卻不會(huì)相沖突。

進(jìn)程需要一些資源才能完成工作吼和,如CPU使用時(shí)間、存儲(chǔ)器骑素、文件以及I/O設(shè)備炫乓,且為依序逐一進(jìn)行刚夺,也就是每個(gè)CPU核心任何時(shí)間內(nèi)僅能運(yùn)行一項(xiàng)進(jìn)程。

進(jìn)程與線程的區(qū)別:進(jìn)程是計(jì)算機(jī)管理運(yùn)行程序的一種方式末捣,一個(gè)進(jìn)程下可包含一個(gè)或者多個(gè)線程侠姑。線程可以理解為子進(jìn)程。

摘自wiki百科

也就是說箩做,進(jìn)程是我們運(yùn)行的程序代碼和占用的資源總和莽红,線程是進(jìn)程的最小執(zhí)行單位,當(dāng)然也支持并發(fā)邦邦“灿酰可以說是把問題細(xì)化,分成一個(gè)個(gè)更小的問題燃辖,進(jìn)而得以解決鬼店。

并且進(jìn)程內(nèi)的線程是共享進(jìn)程資源的,處于同一地址空間黔龟,所以切換和通信相對(duì)成本小妇智,而進(jìn)程可以理解為沒有公共的包裹容器

但是如果進(jìn)程間需要通信的話氏身,也需要一個(gè)公共環(huán)境或者一個(gè)媒介巍棱,這個(gè)就是操作系統(tǒng)。

進(jìn)程演進(jìn)

我們的計(jì)算機(jī)有單核的蛋欣、多核的航徙,也有多種的組合方式:

  1. 單進(jìn)程

因?yàn)槭且粋€(gè)進(jìn)程,所以某一時(shí)刻只能處理一個(gè)事務(wù)豁状,后續(xù)需要等待捉偏,體驗(yàn)不好

  1. 多進(jìn)程

為了解決上面的問題,但是如果有很多請(qǐng)求的話泻红,會(huì)產(chǎn)生很多進(jìn)程夭禽,開銷本身就是一個(gè)不小的問題,而進(jìn)程占據(jù)獨(dú)立的內(nèi)存谊路,這么多響應(yīng)是的進(jìn)程難免會(huì)有重復(fù)的狀態(tài)和數(shù)據(jù)讹躯,會(huì)造成資源浪費(fèi)。

  1. 多進(jìn)程多線程

由之前的進(jìn)程處理事務(wù)缠劝,改成使用線程處理事務(wù)潮梯,解決了開銷大,資源浪費(fèi)的問題惨恭,還可以使用線程池秉馏,預(yù)先創(chuàng)建就緒線程,減少創(chuàng)建和銷毀線程的開銷脱羡。

但是一個(gè)cpu某一時(shí)刻只能處理一個(gè)事務(wù)萝究。像時(shí)間分片來調(diào)度線程的話免都,會(huì)導(dǎo)致線程切換頻繁,是非常耗時(shí)的帆竹。

  1. 單進(jìn)程單線程

類似也就是v8绕娘,基于事件驅(qū)動(dòng),有效的避免了內(nèi)存開銷和上下文切換栽连,只需要線程間通信险领,即可在適當(dāng)?shù)臅r(shí)刻進(jìn)行事務(wù)結(jié)果等的反饋。

但是遇到計(jì)算量很大的事務(wù)秒紧,會(huì)阻塞后續(xù)任務(wù)的執(zhí)行绢陌。像這樣:

image
  1. 單進(jìn)程單線程(多進(jìn)程架構(gòu))

node提供了clusterchild_process兩個(gè)模塊進(jìn)行進(jìn)程的創(chuàng)建,也就是我們常說的主(Master)從(Worker)模式噩茄。Master負(fù)責(zé)任務(wù)調(diào)度和管理Worker進(jìn)程下面,Worker進(jìn)行事務(wù)處理。

image

進(jìn)程間通信

node本身提供了cluster和child_process模塊創(chuàng)建子進(jìn)程绩聘,本質(zhì)上cluster.fork()是child_process.fork()的上層實(shí)現(xiàn)沥割,cluster帶來的好處是可以監(jiān)聽共享端口,否則建議使用child_process凿菩。

child_process

child_process提供了異步和同步的操作方法机杜,具體可查看文檔

常見的異步方法有:

  1. .exec
  2. .execFile
  3. .fork
  4. .spawn

除了fork出來的進(jìn)程會(huì)長期駐存外衅谷,其他方式會(huì)在子進(jìn)程任務(wù)完成后以流的方式返回并銷毀進(jìn)程椒拗。

異步方法會(huì)返回ChildProcess的實(shí)例,ChildProcess不能直接創(chuàng)建获黔,只能返回蚀苛。

來看幾張圖吧:

image
image
image

舉個(gè)例子

有一個(gè)很長很長的循環(huán),如果不開啟子進(jìn)程玷氏,會(huì)等循環(huán)之后才能執(zhí)行之后的邏輯堵未。

我們可以將耗時(shí)的循環(huán)放到子進(jìn)程中,主進(jìn)程會(huì)接受子進(jìn)程的返回盏触,不影響后續(xù)事物的處理渗蟹。

// 主進(jìn)程
const execFile = require('child_process').execFile;

execFile('./child.js', [], (err, stdout, stderr) => {
    if (err) {
        console.log(err);
        return;
    }
    console.log(`stdout: ${stdout}`);
});
console.log('用戶事務(wù)處理');

// 子進(jìn)程
#!/usr/bin/env node

for (let i = 0; i < 10000; i++) {
    process.stdout.write(`${i}`);
}

而對(duì)于fork,它是專門用來生產(chǎn)子進(jìn)程的赞辩,也可以說是主進(jìn)程的拷貝雌芽,返回的ChildProcess中會(huì)內(nèi)置額外的通信通道,也就是IPC通道辨嗽,允許消息在父子進(jìn)程間傳遞世落,例如通過文件描述符,不過由于創(chuàng)建的是匿名通道糟需,所以只有主進(jìn)程可以與之通信岛心,其他進(jìn)程無法進(jìn)行通信来破。但相對(duì)的還有命名通道,詳見下一節(jié)忘古。

看一個(gè)簡單的例子:

//parent.js
const cp = require('child_process');
const n = cp.fork(`${__dirname}/sub.js`);
n.on('message', (m) => {
    console.log('PARENT got message:', m);
});
n.send({ hello: 'world' });

//sub.js
process.on('message', (m) => {
    console.log('CHILD got message:', m);
});
process.send({ foo: 'bar' });

父進(jìn)程通過fork返回的ChildProcess進(jìn)行通信的監(jiān)聽和發(fā)送,子進(jìn)程通過全局變量process進(jìn)行監(jiān)聽和發(fā)送诅诱。

cluster

cluster本質(zhì)上也是通過child_process.fork創(chuàng)建子進(jìn)程髓堪,他還能幫我們合理的管理進(jìn)程。

const cluster = require('cluster');
// 判斷是否為主進(jìn)程
if (cluster.isMaster) {
    const cpuNum = require('os').cpus().length;
    for (let i = 0; i < cpuNum; ++i) {
        cluster.fork();
    }

    cluster.on('online', (worker) => {
        console.log('Create worker-' + worker.process.pid);
    });

    cluster.on('exit', (worker, code, signal) => {
        console.log(
            '[Master] worker ' +
                worker.process.pid +
                ' died with code:' +
                code +
                ', and' +
                signal
        );
        cluster.fork(); // 重啟子進(jìn)程
    });
} else {
    const net = require('net');
    net.createServer()
        .on('connection', (socket) => {
            setTimeout(() => {
                socket.end('Request handled by worker-' + process.pid);
            }, 10);
        })
        .listen(8989);
}

細(xì)心地你可能發(fā)現(xiàn)多個(gè)子進(jìn)程監(jiān)聽了同一個(gè)端口娘荡,這樣不會(huì)EADDRIUNS嗎干旁?

其實(shí)不然,真正監(jiān)聽端口的是主進(jìn)程炮沐,當(dāng)前端請(qǐng)求到達(dá)時(shí)争群,會(huì)將句柄發(fā)送給某個(gè)子進(jìn)程。

理解底層基礎(chǔ)大年,助力上層應(yīng)用

進(jìn)程間通信(IPC)大概有這幾種:

  • 匿名管道
  • 命名管道
  • 信號(hào)量
  • 消息隊(duì)列
  • 信號(hào)
  • 共享內(nèi)存
  • 套接字

從技術(shù)上劃分又可以劃分成以下四種:

  1. 消息傳遞(管道换薄,F(xiàn)IFO,消息隊(duì)列)
  2. 同步(互斥量翔试,條件變量轻要,讀寫鎖等)
  3. 共享內(nèi)存(匿名的,命名的)
  4. 遠(yuǎn)程過程調(diào)用

文件描述符是什么垦缅?

在linux中一切皆文件冲泥,linux會(huì)給每個(gè)文件分配一個(gè)id,這個(gè)id就是文件描述符壁涎,指針也是文件描述符的一種凡恍。這個(gè)很好理解,不過我們可以再往深了說怔球,一個(gè)進(jìn)程啟動(dòng)后嚼酝,會(huì)在內(nèi)核空間(虛擬空間的一部分)創(chuàng)建一個(gè)PCB控制塊,PCB內(nèi)部有一個(gè)文件描述符表庞溜,記錄著當(dāng)前進(jìn)程所有可用的文件描述符(即當(dāng)前進(jìn)程所有打開的文件)革半。系統(tǒng)出了維護(hù)文件描述符表外,還需要維護(hù)打開文件表(Open file table)和i-node表(i-node table)流码。

文件打開表(Open file table)包含文件偏移量又官,狀態(tài)標(biāo)志,i-node表指針等信息

i-node表(i-node table)包括文件類型漫试,文件大小六敬,時(shí)間戳,文件鎖等信息

文件描述符不是一對(duì)一的驾荣,它可以:

  1. 同一進(jìn)程的不同文件描述符指向同一文件
  2. 不同進(jìn)程可以擁有相同的文件描述符(比如fork出的子進(jìn)程擁有和父進(jìn)程一樣的文件描述符外构,或者不同進(jìn)程打開同一文件)
  3. 不同進(jìn)程的同一文件描述符也可以指向不同的文件
  4. 不同進(jìn)程的不同文件描述符也可以指向同一個(gè)文件

上面提及了很多可以實(shí)現(xiàn)進(jìn)程間通信的方式普泡,那node進(jìn)程間通信是以什么為基礎(chǔ)的呢?

nodeIPC通過管道技術(shù) 加 事件循環(huán)方式進(jìn)行通信审编,管道技術(shù)在windows下由命名管道實(shí)現(xiàn)撼班,在*nix系統(tǒng)則由Unix Domain socket實(shí)現(xiàn),提供給我們的是簡單的message事件和send方法垒酬。

那管道是什么呢砰嘁?

管道實(shí)際上是在內(nèi)核中開辟一塊緩沖區(qū),它有一個(gè)讀端一個(gè)寫端勘究,并傳給用戶程序兩個(gè)文件描述符矮湘,一個(gè)指向讀端,一個(gè)指向?qū)懚丝诳诟猓缓笤摼彺鎱^(qū)存儲(chǔ)不同進(jìn)程間寫入的內(nèi)容缅阳,并供不同進(jìn)程讀取內(nèi)容,進(jìn)而達(dá)到通信的目的景描。

管道又分為匿名管道和命名管道十办,匿名管道常見于一個(gè)進(jìn)程fork出子進(jìn)程,只能親緣進(jìn)程通信伏伯,而命名管道可以讓非親緣進(jìn)程進(jìn)行通信橘洞。

image

其實(shí)本質(zhì)上來說進(jìn)程間通信是利用內(nèi)核管理一塊內(nèi)存,不同進(jìn)程可以讀寫這塊內(nèi)容说搅,進(jìn)而可以互相通信炸枣,當(dāng)然,說起來簡單弄唧,做起來難适肠。有興趣的朋友可以自行研究。

進(jìn)程保護(hù)

可以用cluster建立主從進(jìn)程架構(gòu)候引,主進(jìn)程調(diào)度管理和分發(fā)任務(wù)給子進(jìn)程侯养,并在子進(jìn)程掛掉或斷開連接后重啟。

pm2是對(duì)cluster的一種封裝澄干,提供了:

  • 內(nèi)奸負(fù)載均衡
  • 后臺(tái)運(yùn)行
  • 停機(jī)重載
  • 具有Ubuntu逛揩、CentOS的啟動(dòng)腳本
  • 停止不穩(wěn)定的進(jìn)程
  • 控制臺(tái)檢測
  • 有好的可視化界面

具體原理和細(xì)節(jié)以后有空再做分析。

文中若有錯(cuò)誤的地方麸俘,歡迎指出辩稽,我會(huì)及時(shí)更新。希望讀者借鑒的閱讀从媚。

部分圖片來源網(wǎng)絡(luò)逞泄,侵權(quán)立刪

參考鏈接

進(jìn)程、線程、協(xié)程

文件描述符

IPC

IPC2

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末喷众,一起剝皮案震驚了整個(gè)濱河市各谚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌到千,老刑警劉巖昌渤,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異憔四,居然都是意外死亡愈涩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門加矛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人煤篙,你說我怎么就攤上這事斟览。” “怎么了辑奈?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵苛茂,是天一觀的道長。 經(jīng)常有香客問我鸠窗,道長妓羊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任稍计,我火速辦了婚禮躁绸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘臣嚣。我一直安慰自己净刮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布硅则。 她就那樣靜靜地躺著淹父,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怎虫。 梳的紋絲不亂的頭發(fā)上暑认,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音大审,去河邊找鬼蘸际。 笑死,一個(gè)胖子當(dāng)著我的面吹牛饥努,可吹牛的內(nèi)容都是我干的捡鱼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼驾诈!你這毒婦竟也來了缠诅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤乍迄,失蹤者是張志新(化名)和其女友劉穎管引,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闯两,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡褥伴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漾狼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片重慢。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖逊躁,靈堂內(nèi)的尸體忽然破棺而出似踱,到底是詐尸還是另有隱情,我是刑警寧澤稽煤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布核芽,位于F島的核電站,受9級(jí)特大地震影響酵熙,放射性物質(zhì)發(fā)生泄漏轧简。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一匾二、第九天 我趴在偏房一處隱蔽的房頂上張望哮独。 院中可真熱鬧,春花似錦假勿、人聲如沸借嗽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恶导。三九已至,卻和暖如春浸须,著一層夾襖步出監(jiān)牢的瞬間惨寿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國打工删窒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裂垦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓肌索,卻偏偏與公主長得像蕉拢,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354