node ?進(jìn)程管理

前言

node是單線程且支持高并發(fā)的腳本語(yǔ)言,node如何能做到單線程不阻塞挽荡,基于i/o的操作基本都是異步的藐石,node主線程只需發(fā)送異步操作給libuv,由node的工作線程去執(zhí)行定拟,libuv是多線程的線程池用來(lái)并行io操作于微,主線程就可以做其他的事情,直到libuv的返回青自,所以node是i/o非阻塞株依。可以理解node并不是單純的單線程延窜,只是js運(yùn)行是單線程的恋腕。所以node非常適合與I/O密集型的系統(tǒng)。雖然node是開(kāi)啟了多線程逆瑞,但是所有的多線程都是基于node服務(wù)進(jìn)程開(kāi)啟的荠藤,所以并不能充分利用cpu伙单,一個(gè)node實(shí)例,只能利用一個(gè)cpu核心哈肖,故node不適合cpu密集型系統(tǒng)吻育,nodejs是單線程的,進(jìn)行密集型的運(yùn)算會(huì)導(dǎo)致主線程掛起牡彻。

說(shuō)明

  • node 在運(yùn)行時(shí)只生成了一個(gè) javascript 運(yùn)行環(huán)境
  • node 只把 libuv 的 io/timer 接口提供給了 js 引擎
  • js 引擎沒(méi)有異步扫沼,因?yàn)?js 引擎是在 node 的主線程調(diào)用

多進(jìn)程

  • 由于單進(jìn)程單線程,無(wú)法充分利用cpu資源
  • 采用多進(jìn)程去處理庄吼,也就是一個(gè)進(jìn)程利用一個(gè)cpu資源

child_process node提供的核心模塊

// worker 進(jìn)程
'use strict';
const http = require('http');
http.createServer(function(req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello World');
}).listen(Math.round((1 + Math.random()) * 1000), '127.0.0.1');

//master 進(jìn)程
'use strict';
const child_process = require('child_process');
const cpu = require('os').cpus();
for (let i = 0; i < cpu.length; i++) {
  child_process.fork('./worker.js');
}

利用child_process的fork方法缎除,我們可以fork出多個(gè)進(jìn)程。


image.png

目前基于master-work模型如下:


image.png

進(jìn)程ipc

  • 采用master-work模型总寻,需要master對(duì)work進(jìn)程之前進(jìn)行通信
  • fork創(chuàng)建子進(jìn)程后器罐,父進(jìn)程與子進(jìn)程之間將會(huì)創(chuàng)建IPC通道,通過(guò)IPC通道渐行,父子進(jìn)程之間才能通過(guò)message和send()傳遞消息
// master 
'use strict';
const child_process = require('child_process');
const cpu = require('os').cpus();
for (let i = 0; i < cpu.length; i++) {
  let child = child_process.fork('./worker.js');
  child.on('message', function (m) {
    console.log('parent got message:', m);
  });
  child.send({ hello: 'I am parent' });
}

//work
'use strict';
const http = require('http');
http.createServer(function(req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello World');
}).listen(Math.round((1 + Math.random()) * 1000), '127.0.0.1');

process.on('message', function (m) {
  console.log('child got message:', m);
});
process.send({ hello: 'I am child' });
  • 此時(shí)并不能4個(gè)work進(jìn)程監(jiān)聽(tīng)同一個(gè)端口轰坊,通常的做法是,讓master監(jiān)聽(tīng)一個(gè)端口祟印,類似與一個(gè)代理處理負(fù)載均衡肴沫,此時(shí)master與work之間的通信就變得復(fù)雜,node提供的 process.send(message, [sendHandle]) 蕴忆,可以放我們傳入一個(gè)handler句柄颤芬。
// master
'use strict';
const child_process = require('child_process');
const cpu = require('os').cpus();
const server = require('net').createServer();
const works = [];
server.on('connection', function(socket) {
  socket.end('handled by parent');
});
for (let i = 0; i < cpu.length; i++) {
  const child = child_process.fork('./worker.js');
  works.push(child);
}
server.listen(80, function() {
  for (let i = 0; i < works.length; i++) {
    works[i].send('server', server);
  }
  server.close();
});


// work
'use strict';
const http = require('http');
const server = http.createServer(function(req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('handled by child, pid is ' + process.pid + '\n');
});
process.on('message', function(m, tcp) {
  if (m === 'server') {
    tcp.on('connection', function(socket) {
      server.emit('connection', socket);
    });
  }
});
  • 此時(shí)我們的結(jié)構(gòu)如下:
image.png
image.png

這樣就完成了,多個(gè)進(jìn)程同時(shí)監(jiān)聽(tīng)同一個(gè)端口

自動(dòng)重啟

'use strict';
const child_process = require('child_process');
const cpu = require('os').cpus();
const server = require('net').createServer();
server.listen(80);
const workers = {};
server.on('connection', function(socket) {
  socket.end('handled by parent');
});
const createWorker = function() {
  const child = child_process.fork('./worker.js');
  child.send('server', server);
  workers[child.pid] = child;
  console.log('Create worker. pid: ' + child.pid);
  child.on('exit', function() {
    console.log('Worker ' + child.pid + ' exited.');
    delete workers[child.pid];
    createWorker();
  });
};
for (let i = 0; i < cpu.length; i++) {
  createWorker();
}

process.on('exit', function() {
  for (const pid in workers) {
    workers[pid].kill();
  }
}); 
// 當(dāng)某個(gè)work進(jìn)程異常時(shí)套鹅,就會(huì)自動(dòng)?xùn)|西啟動(dòng)一個(gè)新的進(jìn)程處理

數(shù)據(jù)共享

一般有2種方案

  • 數(shù)據(jù)存儲(chǔ)在master中
  • 引用外部存儲(chǔ)機(jī)制如redis站蝠,文件,db

cluster 核心模塊

官方提供用法:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`主進(jìn)程 ${process.pid} 正在運(yùn)行`);

  // 衍生工作進(jìn)程卓鹿。
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`工作進(jìn)程 ${worker.process.pid} 已退出`);
  });
} else {
  // 工作進(jìn)程可以共享任何 TCP 連接菱魔。
  // 在本例子中,共享的是一個(gè) HTTP 服務(wù)器吟孙。
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('你好世界\n');
  }).listen(8000);

  console.log(`工作進(jìn)程 ${process.pid} 已啟動(dòng)`);
}

cluster其實(shí)是net跟child_proccess 的封裝

注意

除非自定義一些進(jìn)程處理機(jī)制澜倦,手動(dòng)管理進(jìn)程的調(diào)度,一般建議用成熟的框架pm2.0杰妓、forever等工具肥隆,無(wú)需手動(dòng)處理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稚失,一起剝皮案震驚了整個(gè)濱河市栋艳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌句各,老刑警劉巖吸占,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晴叨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡矾屯,警方通過(guò)查閱死者的電腦和手機(jī)兼蕊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)件蚕,“玉大人孙技,你說(shuō)我怎么就攤上這事∨抛鳎” “怎么了牵啦?”我有些...
    開(kāi)封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)妄痪。 經(jīng)常有香客問(wèn)我哈雏,道長(zhǎng),這世上最難降的妖魔是什么衫生? 我笑而不...
    開(kāi)封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任裳瘪,我火速辦了婚禮,結(jié)果婚禮上罪针,老公的妹妹穿的比我還像新娘彭羹。我一直安慰自己,他們只是感情好泪酱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布派殷。 她就那樣靜靜地躺著,像睡著了一般西篓。 火紅的嫁衣襯著肌膚如雪愈腾。 梳的紋絲不亂的頭發(fā)上憋活,一...
    開(kāi)封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天岂津,我揣著相機(jī)與錄音,去河邊找鬼悦即。 笑死吮成,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辜梳。 我是一名探鬼主播粱甫,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼作瞄!你這毒婦竟也來(lái)了茶宵?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宗挥,失蹤者是張志新(化名)和其女友劉穎乌庶,沒(méi)想到半個(gè)月后种蝶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞒大,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年螃征,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片透敌。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盯滚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酗电,到底是詐尸還是另有隱情魄藕,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布顾瞻,位于F島的核電站泼疑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏荷荤。R本人自食惡果不足惜退渗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蕴纳。 院中可真熱鬧会油,春花似錦、人聲如沸古毛。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)稻薇。三九已至嫂冻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間塞椎,已是汗流浹背桨仿。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留案狠,地道東北人服傍。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像骂铁,于是被迫代替她去往敵國(guó)和親吹零。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 很多Node.js初學(xué)者都會(huì)有這樣的疑惑拉庵,Node.js到底是單線程的還是多線程的灿椅?通過(guò)本章的學(xué)習(xí),能夠讓讀者較為...
    越努力越幸運(yùn)_952c閱讀 3,632評(píng)論 4 36
  • 前言 通過(guò)前邊的學(xué)習(xí),大家應(yīng)該已經(jīng)充分理解了node的單線程只不過(guò)是js層面的單線程茫蛹,是基于V8引擎的單線程泣懊,因?yàn)?..
    白昔月閱讀 4,545評(píng)論 3 13
  • PM2是一個(gè)帶有負(fù)載均衡功能的Node應(yīng)用的進(jìn)程管理器。PM2可以利用服務(wù)器上的所有CPU麻惶,并保證進(jìn)程永遠(yuǎn)都活著馍刮,...
    涅槃快樂(lè)是金閱讀 3,779評(píng)論 0 1
  • 姥姥家的田野 今年是岳母逝世八周年之際,今以王偉力的作品《姥姥家的...
    朝花夕拾杯中酒123閱讀 417評(píng)論 6 5
  • 以往的假期不是在爺爺奶奶杭州那里度過(guò)窃蹋,就是在姑姑那里度過(guò)卡啰,周末也是在姑姑那里玩耍的呢。 ...
    花燈初夏閱讀 760評(píng)論 0 2