NodeJS能夠感知和控制自身進程的運行環境和狀態,也能夠建立子進程並與其協同工做,這使得NodeJS能夠把多個程序組合在一塊兒共同完成某項工做,並在其中充當膠水和調度器的做用。html
咱們已經知道了NodeJS自帶的fs
模塊比較基礎,把一個目錄裏的全部文件和子目錄都拷貝到另外一個目錄裏須要寫很多代碼。另外咱們也知道,終端下的cp
命令比較好用,一條cp -r source/* target
命令就能搞定目錄拷貝。那咱們首先看看如何使用NodeJS調用終端命令來簡化目錄拷貝,示例代碼以下:node
var child_process = require('child_process'); var util = require('util'); function copy(source, target, callback) { child_process.exec( util.format('cp -r %s/* %s', source, target), callback); } copy('a', 'b', function (err) { // ...
});
從以上代碼中能夠看到,子進程是異步運行的,經過回調函數返回執行結果。api
1、API數組
一、process:官方文檔: http://nodejs.org/api/process.html安全
任何一個進程都有啓動進程時使用的命令行參數,有標準輸入標準輸出,有運行權限,有運行環境和運行狀態。服務器
在NodeJS中,能夠經過process
對象感知和控制NodeJS自身進程的方方面面。app
另外須要注意的是,process
不是內置模塊,而是一個全局對象,所以在任何地方均可以直接使用。異步
二、Child Process:官方文檔: http://nodejs.org/api/child_process.html函數
使用child_process
模塊能夠建立和控制子進程。ui
該模塊提供的API中最核心的是.spawn
,其他API都是針對特定使用場景對它的進一步封裝,算是一種語法糖。
三、Cluster:官方文檔: http://nodejs.org/api/cluster.html
cluster
模塊是對child_process
模塊的進一步封裝,專用於解決單進程NodeJS Web服務器沒法充分利用多核CPU的問題。使用該模塊能夠簡化多進程服務器程序的開發,讓每一個核上運行一個工做進程,並統一經過主進程監聽端口和分發請求。
2、應用場景
一、如何獲取命令行參數
在NodeJS中能夠經過process.argv
獲取命令行參數。
可是比較意外的是,node
執行程序路徑和主模塊文件路徑固定佔據了argv[0]
和argv[1]
兩個位置,而第一個命令行參數從argv[2]
開始。爲了讓argv
使用起來更加天然,能夠按照如下方式處理。
function main(argv) { // ...
} main(process.argv.slice(2));
二、如何退出程序
一般一個程序作完全部事情後就正常退出了,這時程序的退出狀態碼爲0
。或者一個程序運行時發生了異常後就掛了,這時程序的退出狀態碼不等於0
。若是咱們在代碼中捕獲了某個異常,可是以爲程序不該該繼續運行下去,須要當即退出,而且須要把退出狀態碼設置爲指定數字,好比1
,就能夠按照如下方式:
try { // ...
} catch (err) { // ...
process.exit(1); }
三、如何控制輸入輸出
NodeJS程序的標準輸入流(stdin)、一個標準輸出流(stdout)、一個標準錯誤流(stderr)分別對應process.stdin
、process.stdout
和process.stderr
,第一個是隻讀數據流,後邊兩個是隻寫數據流,對它們的操做按照對數據流的操做方式便可。例如,console.log
能夠按照如下方式實現。
function log() { process.stdout.write( util.format.apply(util, arguments) + '\n'); }
四、如何降權
在Linux系統下,咱們知道須要使用root權限才能監聽1024如下端口。可是一旦完成端口監聽後,繼續讓程序運行在root權限下存在安全隱患,所以最好能把權限降下來。如下是這樣一個例子。
http.createServer(callback).listen(80, function () { var env = process.env, uid = parseInt(env['SUDO_UID'] || process.getuid(), 10), gid = parseInt(env['SUDO_GID'] || process.getgid(), 10); process.setgid(gid); process.setuid(uid); });
上例中有幾點須要注意:
若是是經過sudo
獲取root權限的,運行程序的用戶的UID和GID保存在環境變量SUDO_UID
和SUDO_GID
裏邊。若是是經過chmod +s
方式獲取root權限的,運行程序的用戶的UID和GID可直接經過process.getuid
和process.getgid
方法獲取。
process.setuid
和process.setgid
方法只接受number
類型的參數。
降權時必須先降GID再降UID,不然順序反過來的話就沒權限更改程序的GID了。
五、如何建立子進程
如下是一個建立NodeJS子進程的例子。
var child = child_process.spawn('node', [ 'xxx.js' ]); child.stdout.on('data', function (data) { console.log('stdout: ' + data); }); child.stderr.on('data', function (data) { console.log('stderr: ' + data); }); child.on('close', function (code) { console.log('child process exited with code ' + code); });
上例中使用了.spawn(exec, args, options)
方法,該方法支持三個參數。第一個參數是執行文件路徑,能夠是執行文件的相對或絕對路徑,也能夠是根據PATH環境變量能找到的執行文件名。第二個參數中,數組中的每一個成員都按順序對應一個命令行參數。第三個參數可選,用於配置子進程的執行環境與行爲。
另外,上例中雖然經過子進程對象的.stdout
和.stderr
訪問子進程的輸出,但經過options.stdio
字段的不一樣配置,能夠將子進程的輸入輸出重定向到任何數據流上,或者讓子進程共享父進程的標準輸入輸出流,或者直接忽略子進程的輸入輸出。
六、進程間如何通信
在Linux系統下,進程之間能夠經過信號互相通訊。如下是一個例子。
/* parent.js */
var child = child_process.spawn('node', [ 'child.js' ]); child.kill('SIGTERM'); /* child.js */ process.on('SIGTERM', function () { cleanUp(); process.exit(0); });
在上例中,父進程經過.kill
方法向子進程發送SIGTERM
信號,子進程監聽process
對象的SIGTERM
事件響應信號。不要被.kill
方法的名稱迷惑了,該方法本質上是用來給進程發送信號的,進程收到信號後具體要作啥,徹底取決於信號的種類和進程自身的代碼。
另外,若是父子進程都是NodeJS進程,就能夠經過IPC(進程間通信)雙向傳遞數據。如下是一個例子。
/* parent.js */
var child = child_process.spawn('node', [ 'child.js' ], { stdio: [ 0, 1, 2, 'ipc' ] }); child.on('message', function (msg) { console.log(msg); }); child.send({ hello: 'hello' }); /* child.js */ process.on('message', function (msg) { msg.hello = msg.hello.toUpperCase(); process.send(msg); });
能夠看到,父進程在建立子進程時,在options.stdio
字段中經過ipc
開啓了一條IPC通道,以後就能夠監聽子進程對象的message
事件接收來自子進程的消息,並經過.send
方法給子進程發送消息。在子進程這邊,能夠在process
對象上監聽message
事件接收來自父進程的消息,並經過.send
方法向父進程發送消息。數據在傳遞過程當中,會先在發送端使用JSON.stringify
方法序列化,再在接收端使用JSON.parse
方法反序列化。
七、如何守護子進程
守護進程通常用於監控工做進程的運行狀態,在工做進程不正常退出時重啓工做進程,保障工做進程不間斷運行。如下是一種實現方式。
/* daemon.js */ function spawn(mainModule) { var worker = child_process.spawn('node', [ mainModule ]); worker.on('exit', function (code) { if (code !== 0) { spawn(mainModule); } }); } spawn('worker.js');
能夠看到,工做進程非正常退出時,守護進程當即重啓工做進程。