Node之創(chuàng)建多進程應用程序

在Node.js中上陕,只使用一個線程來執(zhí)行所有的操作。因此拓春,如果在應用程序中存在某個操作需要大量消耗CPU資源的情況释簿,則其他操作都會受到一定的影響。例如痘儡,當服務器正在執(zhí)行一個非常消耗CPU資源的操作辕万,則在該操作執(zhí)行之后接收的客戶端請求都需要等待該操作執(zhí)行完畢后才能被處理。沉删、

近些年來,服務器一般都開始使用多核CPU或者多CPU醉途,許多服務器應用程序都開始依靠多線程或多進程機制來處理這些請求矾瑰,以便可以更好地利用這些CPU資源。在Node.js中隘擎,同樣提供一個child_process模塊殴穴。通過該模塊的使用,在Node.js應用程序的主進程運行之后,可以開啟多個子進程采幌。在多個子進程之間可以共享內存空間劲够,可以通過子進程之間的互相通信來實現(xiàn)信息的交換,多個子進程之間也可以通過共享端口的方式將請求分配給多個子進程來執(zhí)行休傍。

使用spawn方法開啟子進程

在child_process模塊中征绎,提供多個可以開啟子進程的方法

首先,可以使用spawn方法開啟一個用于運行某個命令的子進程磨取。

child_process.spawn(command,[args],[options])
  • command:-為一個字符串人柿,用于指定需要運行的命令
  • arg:參數(shù)值為一個數(shù)組,其中存放了所有運行該命令時需要使用的參數(shù)忙厌,參數(shù)的指定順序與數(shù)組中的元素順序保持一致凫岖,如果不使用args參數(shù),默認參數(shù)值為一個空數(shù)組逢净。
  • options:參數(shù)值為一個對象哥放,用于指定開啟子進程時使用的選項。在該對象中爹土,可以指定的屬性及屬性值如下
    • cwd:屬性值為一個字符串婶芭,用于指定子進程的當前工作目錄,可以使用相對路徑或絕對路徑來指定該目錄着饥。
    • stdio:屬性值為一個字符串或一個存放了三個元素的數(shù)組犀农,用于設置子進程的標準輸入/輸出。
    • customFds:屬性值為一個數(shù)組宰掉,用于指定為子進程的標準輸入/輸出指定文件描述符呵哨,目前該屬性值已不推薦使用。
    • env:屬性值為一個對象轨奄,用于以“鍵名/鍵值”的形式為子進程指定環(huán)境變量孟害。不指定該屬性值時子進程中沒有可以使用的環(huán)境變量,而不是使用process.env屬性值中指定的環(huán)境變量挪拟。
    • detached:屬性值為一個布爾值挨务。如果屬性值為true,該子進程為一個進程組中的領頭進程玉组。如果該進程為領頭進程谎柄,當其父進程已不存在時,子進程也可以獨立存在惯雳。該屬性的默認屬性值為false朝巫。
    • uid:屬性值為一個數(shù)值。用于設置子進程的用戶ID石景,只在POSIX(即非Windows)操作系統(tǒng)中有效劈猿。
    • gid:屬性值為一個數(shù)值拙吉。用于設置子進程的組ID,只在POSIX(即非Windows)操作系統(tǒng)中有效揪荣。
    • 'pipe':用于在父進程與子進程之間創(chuàng)建一個管道筷黔。在父進程中可以通過代表子進程的ChildProcess對象的stdio[0]屬性值訪問子進程的標準輸入,可以通過ChildProcess對象的stdio[1]屬性值訪問子進程的標準輸出仗颈,可以通過ChildProcess象的stdio[2]屬性值訪問子進程的標準錯誤輸出佛舱。
    • 'ipc':用于在父進程與子進程之間創(chuàng)建一個專用于傳遞消息或文件描述符的IPC通道。一個子進程最多可以擁有一個IPC通道文件描述符揽乱。設置該選項使子進程的send方法可以被使用名眉。如果子進程在這個文件描述符中寫入JSON格式的消息,將會觸發(fā)子進程對象的message事件凰棉。如果子進程運行Node.js應用程序损拢,IPC通道的存在將使進程對象的send方法可以被使用,也可使進程對象的message事件被觸發(fā)撒犀。
    • 'ignore':用于指定不為子進程設置文件描述符福压。在Node.js中為子進程使用0文件描述符(用于標準輸入)、1文件描述符(用于標準輸出)以及2文件描述(用于標準錯誤輸出)或舞,如果這些文件描述符被忽略荆姆,Node.js將會把子進程的文件描述符定義為/dev/null(重定向到空設備文件)。
    • Stream對象:用于指定子進程與父進程共享一個終端設備映凳、文件胆筒、端口或管道。數(shù)據(jù)流的底層文件描述符將在子進程中被復制诈豌。
    • 正整數(shù)值:用于指定父進程中被打開的文件描述符仆救。該文件描述符在子進程中被共享,其效果與在父進程及子進程中共享一個Stream對象一樣矫渔。
    • null或undefined:使用默認值彤蔽。對于標準輸入/輸出來說,在子進程中創(chuàng)建與父進程相連接的管道庙洼。對于用于讀取文件的文件描述符3來說顿痪,在子進程中被忽略。

spawn方法返回一個隱式創(chuàng)建的代表子進程的ChildProcess對象油够。

spawn使用示例:

var cp=require('child_process');
var sp1 =cp.spawn('node',['test1.js','one','two','three','four'],{cwd:'./test'});
var sp2 =cp.spawn('node',['test2.js'],{stdio:'pipe'});
sp1.stdout.on('data',function (data) {
    console.log('子進程標準輸出: '+data);
    sp2.stdin.write(data);
});
sp1.on('exit',function(code,signal) {
    console.log('子進程退出蚁袭,退出代碼為:'+code);
    process.exit();
});

父進程向子進程發(fā)送信號

在父進程中,可以使用子進程對象的kill方法向子進程發(fā)送信號叠聋。

child.kill([signal])

可以使用一個可選參數(shù)撕阎,參數(shù)值為用于描述該信號的字符串。默認參數(shù)值為’SIGTERM’(用于強制關閉進程)碌补。

使用子進程的kill方法關閉子進程

var cp=require('child_process');
var sp1 =cp.spawn('node',['test1.js','one','two','three','four'],{cwd:'./test'});
var sp2 =cp.spawn('node',['test2.js'],{stdio:'pipe'});
sp1.stdout.on('data',function (data) {
    console.log('子進程標準輸出: '+data);
    sp2.stdin.write(data);
    sp1.kill();
});
sp1.on('exit',function(code,signal) {
    if(!code)
    console.log('子進程退出虏束,退出信號為:'+signal);
    else
    console.log('子進程退出,退出代碼為:'+code);
    process.exit();
});
sp1.on('error',function (err) {
    console.log('子進程開啟失敗: '+err);
    process.exit();
});

如果利用spawn方法的options參數(shù)值對象的stdio屬性中的'ipc'字符串值在父進程與子進程之間創(chuàng)建一個IPC通道厦章,那么镇匀,當該通道關閉時,將觸發(fā)子進程對象的disconnect事件袜啃。

var cp=require('child_process');
var sp1 =cp.spawn('node',['test1.js','one','two','three','four'],{cwd:'./test',stdio:['ipc','pipe','ignore']});
var sp2 =cp.spawn('node',['test2.js'],{stdio:['pipe']});
sp1.stdout.on('data',function (data) {
    console.log('子進程標準輸出: '+data);
    sp2.stdin.write(data);
});
sp1.on('exit',function(code,signal) {
    console.log('子進程退出汗侵,退出代碼為:'+code);
    process.exit();
});
sp1.on('error',function (err) {
    console.log('子進程開啟失敗: '+err);
    process.exit();
});
sp1.on('disconnect', function() {
    console.log('IPC通道被關閉。');
});

使用fork方法開啟子進程

在child_process模塊中群发,可以使用fork方法開啟一個專用于運行Node.js中的某個模塊文件的子進程晰韵。

child_process.fork(modulePath,[args],[options])
  • modulePath:參數(shù)值為一個字符串,用于指定需要運行的Node.js模塊文件路徑及文件名熟妓。
  • arg:參數(shù)值為一個數(shù)組雪猪,其中存放了所有運行該模塊文件時需要使用的參數(shù),參數(shù)的指定順序與數(shù)組中的元素順序保持一致起愈,如果不使用args參數(shù)只恨,默認參數(shù)值為一個空數(shù)組。
  • options:參數(shù)值為一個對象抬虽,用于指定開啟子進程時使用的選項
    • cwd:屬性值為一個字符串官觅,用于指定子進程的當前工作目錄,可以使用相路徑或絕對路徑來指定該目錄阐污。
    • env:屬性值為一個對象休涤,用于以“鍵名/鍵值”的形式為子進程指定環(huán)境變量。不指定該屬性值時子進程中沒有可以使用的環(huán)境變量笛辟,而不是使用process.env屬性值中指定的環(huán)境變量功氨。
    • encoding:屬性值為一個字符串,用于指定標準輸出及標準錯誤輸出數(shù)據(jù)的編碼格式隘膘。默認屬性值為“utf8”疑故。
    • silent:屬性值為一個布爾值,當屬性值為false時弯菊,子進程對象與父進程對象共享標準輸入/輸出纵势,當屬性值為true時,子進程對象與父進程對象不共享標準輸入/輸出管钳。默認屬性值為false钦铁。

fork方法返回一個隱式創(chuàng)建的代表子進程的ChildProcess對象。

在使用fork方法時才漆,當子進程中所有輸入/輸出操作執(zhí)行完畢后牛曹,子進程不會自動退出。必須使用process.exit()方法將其顯式退出醇滥。

當使用fork方法開啟子進程后黎比,可以使用子進程對象的send方法在父進程中向子進程發(fā)送消息超营,在子進程中也可以使用進程對象的send方法向父進程發(fā)送消息,

child.send(message,[sendHandle]) //
在父進程中向子進程發(fā)送消息
process.send(message,[sendHandle]) //
在父進程中向主進程發(fā)送消息
  • message:參數(shù)值為一個對象阅虫,用于指定需要發(fā)送的消息演闭。
  • sendHandle:參數(shù)可以指定為一個當接收到對方發(fā)送消息后執(zhí)行的回調函數(shù),也可以為一個服務器對象或socket端口對象颓帝,以達到在父進程與子進程之間共享該對象的目的米碰。

子父進程共享HTTP服務器

父進程

var http = require('http');
var child_process = require('child_process');
var fs = require('fs');
var child = child_process.fork('child.js');
var server = net.createServer();
server.listen(1337, '127.0.0.1', function () {
    child.send('server', server);
    console.log('父進程中的服務器已創(chuàng)建。');
    var httpServer = http.createServer();
    httpServer.on('request', function (req, res) {
        if(req.url!=="/favicon.ico"){
        var sum=0;
        for(var i=0;i<1000000;i++){
            sum+=i;
        }
        res.write("客戶端請求在父進程中被處理购城。");
        res.end("sum="+sum);
    }
    });
    httpServer.listen(server);
});

子進程

var http = require('http');
process.on('message', function (msg,server) {
    if (msg === 'server') {
    console.log('子進程中的服務器已創(chuàng)建吕座。');
    var httpServer = http.createServer();
    httpServer.on('request', function (req, res) {
        if(req.url!=="/favicon.ico"){
            sum=0;
            for(var i=0;i<1000000;i++){
            sum+=i;
        }
        res.write("客戶端請求在子進程中被處理。");
        res.end("sum="+sum);
      }
    });
    httpServer.listen(server);
   }
});

連續(xù)向服務端發(fā)送10次請求

var http = require('http');
var options = {
    hostname: 'localhost',
    port: 1337,
    path: '/',
    method: 'GET'
};
for(var i=0;i<10;i++){
    var req = http.request(options,function(res) {
        res.on('data', function (chunk) {
            console.log('響應內容: '+chunk);
        });
    });
    req.end();
}

父進程與子進程共享socket端口對象

父進程

var child = require('child_process').fork('child.js');
var server = require('net').createServer();
server.on('connection', function(socket) {
    if (socket.remoteAddress!== '192.168.1.100') {
        child.send('socket', socket);
    return;
}
socket.end('客戶端請求被父進程處理瘪板。');
});
server.listen(42367,'192.168.1.100');

子進程

process.on('message', function(m,socket) {
 if (m === 'socket') {
    socket.end('客戶端請求被子進程處理吴趴。');
 }
});

使用exec方法開啟子進程

在child_process模塊中,可以使用exec方法開啟一個用于運行某個命令的子進程并緩存子進程中的輸出結果篷帅。

child_process.exec(command,[options],[callback])
  • command:參數(shù)值為一個字符串史侣,用于指定需要運行的命令。
  • options:參數(shù)值為一個對象魏身,用于指定開啟子進程時使用的選項惊橱。
    • cwd:屬性值為一個字符串,用于指定子進程的當前工作目錄箭昵,可以使用相對路徑或絕對路徑來指定該目錄税朴。默認屬性值為null。
    • env:屬性值為一個對象家制,用于以“鍵名/鍵值”的形式為子進程指定環(huán)境變量正林。不指定該屬性值時,子進程中沒有可以使用的環(huán)境變量颤殴,而不是使用process.env屬性值中指定的環(huán)境變量觅廓。默認屬性值為null。
    • encoding:屬性值為一個字符串涵但,用于指定標準輸出及標準錯誤輸出數(shù)據(jù)的編碼格式杈绸。默認屬性值為“utf8”。
    • timeout:屬性值為一個整數(shù)值矮瘟,用于指定子進程的超時時間瞳脓,單位為毫秒。當子進程的運行時間超過該時間時澈侠,Node.js將使用killSignal屬性值所指定的信號強制關閉該子進程劫侧。默認屬性值為0,表示不限定子進程的運行時間。
    • maxbuffer:屬性值為一個整數(shù)值烧栋,用于指定用于緩存標準輸出數(shù)據(jù)及標準錯誤輸出數(shù)據(jù)的緩存區(qū)的最大長度写妥,如果標準輸出數(shù)據(jù)及標準錯誤輸出數(shù)據(jù)的總長度超過該屬性值,子進程將被強制關閉劲弦。默認屬性值為200*1024耳标。
    • killSignal:用于指定關閉子進程的信號醇坝,默認屬性值為'SIGTERM'邑跪。

exec方法中使用的callback參數(shù)用于指定子進程終止時調用的回調函數(shù)

function(error, stdout, stderr) {
    //回調函數(shù)代碼略
}
  • error:參數(shù)值為子進程被異常終止時觸發(fā)的異常對象。
  • stderr:為緩存了子進程標準錯誤輸出數(shù)據(jù)的緩存區(qū)對象呼猪。
  • stdout:為緩存了子進程標準輸出數(shù)據(jù)的緩存區(qū)對象

exec方法與spawn方法的最大區(qū)別是画畅,spawn方法可以在父進程中實時接收子進程中輸出的標準輸出流數(shù)據(jù)或標準錯誤輸出流數(shù)據(jù),因此是一個異步方法宋距。如果使用exec方法轴踱,那么父進程必須等待子進程中的標準輸出流數(shù)據(jù)或標準錯誤輸出流數(shù)據(jù)全部緩存完畢后才能接收這些數(shù)據(jù),因此是一個同步方法谚赎。

使用execFile方法開啟子進程

在child_process模塊中淫僻,可以使用execFile方法開啟一個專用于運行某個可執(zhí)行文件的子進程。

child_process.execFile(file,[args],[options],[callback])
  • file:參數(shù)值為一個字符串壶唤,用于指定需要運行的可執(zhí)行文件路徑及文件名雳灵。
  • args:參數(shù)值為一個數(shù)組,其中存放了所有運行該文件時需要使用的參數(shù)闸盔,參數(shù)的指定順序與數(shù) 組中的元素順序保持一致悯辙,如果不使用args參數(shù),默認參數(shù)值為一個空數(shù)組迎吵。
  • options:參數(shù)值為一個對象躲撰,用于指定開啟子進程時使用的選項。在該對象中击费,可以指定的屬性及屬性值與exec方法中所使用的options參數(shù)值對象中可以指定的屬性及屬性值完全相同拢蛋。
  • callback:用于指定子進程終止時調用的回調函數(shù),該回調函數(shù)的指定方法與exec方法中使用的callback參數(shù)值回調函數(shù)的指定方法完全相同蔫巩。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末谆棱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子批幌,更是在濱河造成了極大的恐慌础锐,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荧缘,死亡現(xiàn)場離奇詭異皆警,居然都是意外死亡,警方通過查閱死者的電腦和手機截粗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門信姓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸵隧,“玉大人,你說我怎么就攤上這事意推《固保” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵菊值,是天一觀的道長外驱。 經(jīng)常有香客問我,道長腻窒,這世上最難降的妖魔是什么昵宇? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮儿子,結果婚禮上瓦哎,老公的妹妹穿的比我還像新娘。我一直安慰自己柔逼,他們只是感情好蒋譬,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愉适,像睡著了一般犯助。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上儡毕,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天也切,我揣著相機與錄音,去河邊找鬼腰湾。 笑死雷恃,一個胖子當著我的面吹牛,可吹牛的內容都是我干的费坊。 我是一名探鬼主播倒槐,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼附井!你這毒婦竟也來了讨越?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤永毅,失蹤者是張志新(化名)和其女友劉穎把跨,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沼死,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡着逐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耸别。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡健芭,死狀恐怖,靈堂內的尸體忽然破棺而出秀姐,到底是詐尸還是另有隱情慈迈,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布省有,位于F島的核電站痒留,受9級特大地震影響,放射性物質發(fā)生泄漏锥咸。R本人自食惡果不足惜狭瞎,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搏予。 院中可真熱鬧,春花似錦弧轧、人聲如沸雪侥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽速缨。三九已至,卻和暖如春代乃,著一層夾襖步出監(jiān)牢的瞬間旬牲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工搁吓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留原茅,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓堕仔,卻偏偏與公主長得像擂橘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子摩骨,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容

  • 六胡是開著寶馬而穿NewBalance唯一的人通贞。他身材中等;青白臉色恼五,皺紋間時常夾些傷痕昌罩。穿的雖然是NB,可是又臟...
    曬太陽的老人閱讀 184評論 2 1
  • 繡花鞋墊的回憶 作者灾馒,夏天的茉莉 時間過的很快茎用,一晃又到婆母祭日,很久不愿再寫悲傷,今夜提筆又濕眼眶…… 十三年前...
    夏天的茉莉閱讀 1,588評論 34 46
  • 我們每個人都是不完美的绘搞,我們每個人的行動也都是不完美的彤避。我們在追求極致的完美其實是一種不敢向前的借口。一個勇于向前...
    蘇益民閱讀 79評論 0 0