Node之創建多進程應用程序

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

近些年來,服務器一般都開始使用多核CPU或者多CPU,許多服務器應用程序都開始依靠多線程或多進程機制來處理這些請求,以便可以更好地利用這些CPU資源。在Node.js中,同樣提供一個child_process模塊。通過該模塊的使用,在Node.js應用程序的主進程運行之後,可以開啓多個子進程。在多個子進程之間可以共享內存空間,可以通過子進程之間的互相通信來實現信息的交換,多個子進程之間也可以通過共享端口的方式將請求分配給多個子進程來執行。

使用spawn方法開啓子進程

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

首先,可以使用spawn方法開啓一個用於運行某個命令的子進程。

child_process.spawn(command,[args],[options])
  • command:-爲一個字符串,用於指定需要運行的命令
  • arg:參數值爲一個數組,其中存放了所有運行該命令時需要使用的參數,參數的指定順序與數組中的元素順序保持一致,如果不使用args參數,默認參數值爲一個空數組。
  • options:參數值爲一個對象,用於指定開啓子進程時使用的選項。在該對象中,可以指定的屬性及屬性值如下
    • cwd:屬性值爲一個字符串,用於指定子進程的當前工作目錄,可以使用相對路徑或絕對路徑來指定該目錄。
    • stdio:屬性值爲一個字符串或一個存放了三個元素的數組,用於設置子進程的標準輸入/輸出。
    • customFds:屬性值爲一個數組,用於指定爲子進程的標準輸入/輸出指定文件描述符,目前該屬性值已不推薦使用。
    • env:屬性值爲一個對象,用於以“鍵名/鍵值”的形式爲子進程指定環境變量。不指定該屬性值時子進程中沒有可以使用的環境變量,而不是使用process.env屬性值中指定的環境變量。
    • detached:屬性值爲一個布爾值。如果屬性值爲true,該子進程爲一個進程組中的領頭進程。如果該進程爲領頭進程,當其父進程已不存在時,子進程也可以獨立存在。該屬性的默認屬性值爲false。
    • uid:屬性值爲一個數值。用於設置子進程的用戶ID,只在POSIX(即非Windows)操作系統中有效。
    • gid:屬性值爲一個數值。用於設置子進程的組ID,只在POSIX(即非Windows)操作系統中有效。
    • ‘pipe’:用於在父進程與子進程之間創建一個管道。在父進程中可以通過代表子進程的ChildProcess對象的stdio[0]屬性值訪問子進程的標準輸入,可以通過ChildProcess對象的stdio[1]屬性值訪問子進程的標準輸出,可以通過ChildProcess象的stdio[2]屬性值訪問子進程的標準錯誤輸出。
    • ‘ipc’:用於在父進程與子進程之間創建一個專用於傳遞消息或文件描述符的IPC通道。一個子進程最多可以擁有一個IPC通道文件描述符。設置該選項使子進程的send方法可以被使用。如果子進程在這個文件描述符中寫入JSON格式的消息,將會觸發子進程對象的message事件。如果子進程運行Node.js應用程序,IPC通道的存在將使進程對象的send方法可以被使用,也可使進程對象的message事件被觸發。
    • ‘ignore’:用於指定不爲子進程設置文件描述符。在Node.js中爲子進程使用0文件描述符(用於標準輸入)、1文件描述符(用於標準輸出)以及2文件描述(用於標準錯誤輸出),如果這些文件描述符被忽略,Node.js將會把子進程的文件描述符定義爲/dev/null(重定向到空設備文件)。
    • Stream對象:用於指定子進程與父進程共享一個終端設備、文件、端口或管道。數據流的底層文件描述符將在子進程中被複制。
    • 正整數值:用於指定父進程中被打開的文件描述符。該文件描述符在子進程中被共享,其效果與在父進程及子進程中共享一個Stream對象一樣。
    • null或undefined:使用默認值。對於標準輸入/輸出來說,在子進程中創建與父進程相連接的管道。對於用於讀取文件的文件描述符3來說,在子進程中被忽略。

spawn方法返回一個隱式創建的代表子進程的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();
});

父進程向子進程發送信號

在父進程中,可以使用子進程對象的kill方法向子進程發送信號。

child.kill([signal])

可以使用一個可選參數,參數值爲用於描述該信號的字符串。默認參數值爲’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參數值對象的stdio屬性中的’ipc’字符串值在父進程與子進程之間創建一個IPC通道,那麼,當該通道關閉時,將觸發子進程對象的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:參數值爲一個字符串,用於指定需要運行的Node.js模塊文件路徑及文件名。
  • arg:參數值爲一個數組,其中存放了所有運行該模塊文件時需要使用的參數,參數的指定順序與數組中的元素順序保持一致,如果不使用args參數,默認參數值爲一個空數組。
  • options:參數值爲一個對象,用於指定開啓子進程時使用的選項
    • cwd:屬性值爲一個字符串,用於指定子進程的當前工作目錄,可以使用相路徑或絕對路徑來指定該目錄。
    • env:屬性值爲一個對象,用於以“鍵名/鍵值”的形式爲子進程指定環境變量。不指定該屬性值時子進程中沒有可以使用的環境變量,而不是使用process.env屬性值中指定的環境變量。
    • encoding:屬性值爲一個字符串,用於指定標準輸出及標準錯誤輸出數據的編碼格式。默認屬性值爲“utf8”。
    • silent:屬性值爲一個布爾值,當屬性值爲false時,子進程對象與父進程對象共享標準輸入/輸出,當屬性值爲true時,子進程對象與父進程對象不共享標準輸入/輸出。默認屬性值爲false。

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

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

當使用fork方法開啓子進程後,可以使用子進程對象的send方法在父進程中向子進程發送消息,在子進程中也可以使用進程對象的send方法向父進程發送消息,

child.send(message,[sendHandle]) //
在父進程中向子進程發送消息
process.send(message,[sendHandle]) //
在父進程中向主進程發送消息
  • message:參數值爲一個對象,用於指定需要發送的消息。
  • sendHandle:參數可以指定爲一個當接收到對方發送消息後執行的回調函數,也可以爲一個服務器對象或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('父進程中的服務器已創建。');
    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('子進程中的服務器已創建。');
    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);
   }
});

連續向服務端發送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:參數值爲一個字符串,用於指定需要運行的命令。
  • options:參數值爲一個對象,用於指定開啓子進程時使用的選項。
    • cwd:屬性值爲一個字符串,用於指定子進程的當前工作目錄,可以使用相對路徑或絕對路徑來指定該目錄。默認屬性值爲null。
    • env:屬性值爲一個對象,用於以“鍵名/鍵值”的形式爲子進程指定環境變量。不指定該屬性值時,子進程中沒有可以使用的環境變量,而不是使用process.env屬性值中指定的環境變量。默認屬性值爲null。
    • encoding:屬性值爲一個字符串,用於指定標準輸出及標準錯誤輸出數據的編碼格式。默認屬性值爲“utf8”。
    • timeout:屬性值爲一個整數值,用於指定子進程的超時時間,單位爲毫秒。當子進程的運行時間超過該時間時,Node.js將使用killSignal屬性值所指定的信號強制關閉該子進程。默認屬性值爲0,表示不限定子進程的運行時間。
    • maxbuffer:屬性值爲一個整數值,用於指定用於緩存標準輸出數據及標準錯誤輸出數據的緩存區的最大長度,如果標準輸出數據及標準錯誤輸出數據的總長度超過該屬性值,子進程將被強制關閉。默認屬性值爲200*1024。
    • killSignal:用於指定關閉子進程的信號,默認屬性值爲’SIGTERM’。

exec方法中使用的callback參數用於指定子進程終止時調用的回調函數

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

exec方法與spawn方法的最大區別是,spawn方法可以在父進程中實時接收子進程中輸出的標準輸出流數據或標準錯誤輸出流數據,因此是一個異步方法。如果使用exec方法,那麼父進程必須等待子進程中的標準輸出流數據或標準錯誤輸出流數據全部緩存完畢後才能接收這些數據,因此是一個同步方法。

使用execFile方法開啓子進程

在child_process模塊中,可以使用execFile方法開啓一個專用於運行某個可執行文件的子進程。

child_process.execFile(file,[args],[options],[callback])
  • file:參數值爲一個字符串,用於指定需要運行的可執行文件路徑及文件名。
  • args:參數值爲一個數組,其中存放了所有運行該文件時需要使用的參數,參數的指定順序與數 組中的元素順序保持一致,如果不使用args參數,默認參數值爲一個空數組。
  • options:參數值爲一個對象,用於指定開啓子進程時使用的選項。在該對象中,可以指定的屬性及屬性值與exec方法中所使用的options參數值對象中可以指定的屬性及屬性值完全相同。
  • callback:用於指定子進程終止時調用的回調函數,該回調函數的指定方法與exec方法中使用的callback參數值回調函數的指定方法完全相同。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章