欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Node.js中創(chuàng)建和管理外部進程詳解

 更新時間:2014年08月16日 11:13:30   作者:Jack Yao  
這篇文章主要介紹了Node.js中創(chuàng)建和管理外部進程詳解,本文講解了執(zhí)行外部命令的方法、子進程相關內(nèi)容等,需要的朋友可以參考下

Node被設計用來高效的處理I/O操作,但是你應該知道,有些類型的程序并不適合這種模式。比如,如果你打算用Node處理一個CPU密集的任務,你可能會堵塞事件循環(huán),并因此降低了程序的響應。替代辦法是,把CPU密集的任務分配給一個單獨的進程來處理,從而釋放事件循環(huán)。Node允許你產(chǎn)生進程,并把這個新進程做為它父進程的子進程。在Node里,子進程可以和父進程進行雙向通信,而且在某種程度上,父進程還可以監(jiān)控和管理子進程。

另外一種需要使用子進程的情況是,當你想簡單地執(zhí)行一個外部命令,并讓Node獲取命令的返回值時。比如,你可以執(zhí)行一個UNIX命令、腳本或者其他那些不能在Node里直接執(zhí)行的命令。

本章將向你展示如何執(zhí)行外部命令,創(chuàng)建,并和子進程通信,以及終結(jié)子進程。重點是讓你了解如何在Node進程外完成一系列任務。

執(zhí)行外部命令

當你需要執(zhí)行一個外部shell命令或可執(zhí)行文件時,你可以使用child_process模塊,像這樣導入它:

復制代碼 代碼如下:

var child_process = require(‘child_process')

然后可以用模塊內(nèi)的exec函數(shù)來執(zhí)行外部命令:
復制代碼 代碼如下:

var exec = child_process.exec;

exec(command,callback);


exec的第一個參數(shù)是你準備執(zhí)行的shell命令字符串,第二個參數(shù)是一個回調(diào)函數(shù)。這個回調(diào)函數(shù)將會在exec執(zhí)行完外部命令或者有錯誤發(fā)生時被調(diào)用?;卣{(diào)函數(shù)有三個參數(shù):error,stdout,stderr,看下面的例子:
復制代碼 代碼如下:

exec(‘ls',function(err,stdout,stderr){

         //譯者注:如果使用windows,可改為windows命令,比如dir,后面不再贅述

});

如果有錯誤發(fā)生,第一個參數(shù)將會是一個Error類的實例,如果第一個參數(shù)不包含錯誤,那么第二個參數(shù)stdout將會包含命令的標準輸出。最后一個參數(shù)包含命令相關的錯誤輸出。

列表8-1 展示了一個復雜些的執(zhí)行外部命令的例子

LISTING 8-1:執(zhí)行外部命令(源碼:chapter8/01_external_command.js)

復制代碼 代碼如下:

//導入child_process模塊的exec函數(shù)
var exec = require(‘child_process').exec;
//調(diào)用“cat *.js | wc -l”命令
exec(‘cat *.js | wc –l ‘, function(err, stdout, stderr ){  //第四行
         //命令退出或者調(diào)用失敗
         if( err ){
              //啟動外部進程失敗
              console.log(‘child_process 退出,錯誤碼是:',err.code);
              return;
         }
}

第四行,我們把“cat *.js | wc -l”作為第一個參數(shù)傳遞給exec,你也可以嘗試任何其它命令,只要你在shell里使用過的命令都可以。

然后將一個回調(diào)函數(shù)作為第二個參數(shù),它將會在錯誤發(fā)生或者子進程終結(jié)的時候被調(diào)用。

還可以在回調(diào)函數(shù)之前傳遞第三個可選參數(shù),它包含一些配置選項,比如:

復制代碼 代碼如下:

var exec = require(‘child_process').exec;       

var options ={
         timeout: 1000,
         killSignal: ‘SIGKILL'
     };

exec(‘cat *.js | wc –l ‘, options, function(err,stdout,stderr){
         //…
});

可以使用的參數(shù)有:

1.cwd —— 當前目錄,可以指定當前工作目錄。
2.encoding —— 子進程輸出內(nèi)容的編碼格式,默認值是”utf8”,也就是UTF-8編碼。如果子進程的輸出不是utf8,你可以用這個參數(shù)來設置,支持的編碼格式有:

復制代碼 代碼如下:

ascii
utf8
ucs2
base64

如果你想了解Node支持的這些編碼格式的更多信息,請參考第4章“使用Buffer處理,編碼,解碼二進制數(shù)據(jù)”。

1.timeout —— 以毫秒為單位的命令執(zhí)行超時時間,默認是0,即無限制,一直等到子進程結(jié)束。
2.maxBuffer —— 指定stdout流和stderr流允許輸出的最大字節(jié)數(shù),如果達到最大值,子進程會被殺死。默認值是200*1024。
3.killSignal —— 當超時或者輸出緩存達到最大值時發(fā)送給子進程的終結(jié)信號。默認值是“SIGTERM”,它將給子進程發(fā)送一個終結(jié)信號。通常都會使用這種有序的方式來結(jié)束進程。當用SIGTERM信號時,進程接收到以后還可以進行處理或者重寫信號處理器的默認行為。如果目標進程需要,你可以同時向他傳遞其它的信號(比如SIGUSR1)。你也可以選擇發(fā)送一個SIGKILL信號,它會被操作系統(tǒng)處理并強制立刻結(jié)束子進程,這樣的話,子進程的任何清理操作都不會被執(zhí)行。

如果你想更進一步的控制進程的結(jié)束,可以使用child_process.spawn命令,后面會介紹。

1.evn —— 指定傳遞給子進程的環(huán)境變量,默認是null,也就是說子進程會繼承在它被創(chuàng)建之前的所有父進程的環(huán)境變量。

注意:使用killSignal選項,你可以以字符串的形式向目標進程發(fā)送信號。在Node里信號以字符串的形式存在,下面是UNIX信號和對應默認操作的列表:

你可能想為子進程提供一組可擴展的父級環(huán)境變量。如果直接去修改process.env對象,你會改變Node進程內(nèi)所有模塊的環(huán)境變量,這樣會惹很多麻煩。替代方案是,創(chuàng)建一個新對象,復制process.env里的所有參數(shù),見例子8-2:

LISTING 8-2:使用參數(shù)化的環(huán)境變量來執(zhí)行命令(源碼:chapter8/02_env_vars_augment.js)

復制代碼 代碼如下:

var env = process.env,
    varName,
    envCopy = {},
    exec = require(‘child_prcess').exec;
//將process.env復制到envCopy
for( vaName in ev){
    envCopy[varName] = env[varName];
}

//設置一些自定義變量
envCopy[‘CUSTOM ENV VAR1'] = ‘some value';
envCopy[‘CUSTOM ENV VAR2'] = ‘some other value';

//使用process.env和自定義變量來執(zhí)行命令
exec(‘ls –la',{env: envCopy}, function(err,stdout,stderr){
    if(err){ throw err; }
    console.log(‘stdout:', stdout);
    console.log(‘stderr:',stderr);
}

上面例子,創(chuàng)建了一個用來保存環(huán)境變量的envCopy變量,它首先從process.env那里復制Node進程的環(huán)境變量,然后又添加或替換了一些需要修改的環(huán)境變量,最后把envCopy作為環(huán)境變量參數(shù)傳遞給exec函數(shù)并執(zhí)行外部命令。

記住,環(huán)境變量是通過操作系統(tǒng)在進程之間傳遞的,所有類型的環(huán)境變量值都是以字符串的形式到達子進程的。比如,如果父進程將數(shù)字123作為一個環(huán)境變量,子進程將會以字符串的形式接收“123”。

下面的例子,將在同一個目錄里建立2個Node腳本:parent.js和child.js,第一個腳本將會調(diào)用第二個,下面我們來創(chuàng)建這兩個文件:

LISTING 8-3: 父進程設置環(huán)境變量(chapter8/03_environment_number_parent.js)

復制代碼 代碼如下:

var exec = require('child_process').exec;

exec('node child.js', {env: {number: 123}}, function(err, stdout, stderr) {

if (err) { throw err; }

    console.log('stdout:\n', stdout);

    console.log('stderr:\n', stderr);

});

把這段代碼保存到parent.js,下面是子進程的源碼,把它們保存到child.js(見例8-4)

例 8-4: 子進程解析環(huán)境變量(chapter8/04_environment_number_child.js)

復制代碼 代碼如下:

var   number = process.env.number;

console.log(typeof(number)); // → "string"

number = parseInt(number, 10);

console.log(typeof(number)); // → "number"

當你把這個文件保存為child.js后,就可以在這個目錄下運行下面的命令:

復制代碼 代碼如下:

$ node parent.js

將會看到如下的輸出:

復制代碼 代碼如下:

sdtou:

string

number

stderr:

可以看到,盡管父進程傳遞了一個數(shù)字型的環(huán)境變量,但是子進程卻以字符串形式接收它(參見輸出的第二行),在第三行你把這個字符串解析成了一個數(shù)字。

生成子進程

如你所見,可以使用child_process.exec()函數(shù)來啟動外部進程,并在進程結(jié)束的時候調(diào)用你的回調(diào)函數(shù),這樣用起來很簡單,不過也有一些缺點:

1.除了使用命令行參數(shù)和環(huán)境變量,使用exec()無法和子進程通信
2.子進程的輸出是被緩存的,因此你無法流化它,它可能會耗盡內(nèi)存

幸運的是,Node的child_process模塊允許更細粒度的控制子進程的啟動,停止,及其它常規(guī)操作。你可以在應用程序里啟動一個新的子進程,Node提供一個雙向的通信通道,可以讓父進程和子進程相互收發(fā)字符串數(shù)據(jù)。父進程還可以有些針對子進程的管理操作,給子進程發(fā)送信號,以及強制關閉子進程。

創(chuàng)建子進程

你可以使用child_process.spawn函數(shù)來創(chuàng)建一個新的子進程,見例8-5:

例 8-5: 生成子進程。 (chapter8/05_spawning_child.js)

復制代碼 代碼如下:

// 導入child_process模塊的spawn函數(shù)

var spawn = require('child_process').spawn;

// 生成用來執(zhí)行 "tail -f /var/log/system.log"命令的子進程

var child = spawn('tail', ['-f', '/var/log/system.log']);

上面代碼生成了一個用來執(zhí)行tail命令的子進程,并將“-f”和“/bar/log/system.log”作為參數(shù)。tail命令將會監(jiān)控/var/log/system.og文件(如果存在的話),然后將所有追加的新數(shù)據(jù)輸出到stdout標準輸出流。spawn函數(shù)返回一個ChildProcess對象,它是一個指針對象,封裝了真實進程的訪問接口。這個例子里我們把這個新的描述符賦值給一個叫做child的變量。

監(jiān)聽來自子進程的數(shù)據(jù)

任何包含stdout屬性的子進程句柄,都會將子進程的標準輸出stdout作為一個流對象,你可以在這個流對象上綁定data事件,這樣每當有數(shù)據(jù)塊可用時,就會調(diào)用對應的回調(diào)函數(shù),見下面的例子:

復制代碼 代碼如下:

 //將子進程的輸出打印到控制臺

child.stdout.on(‘data',function(data){

         console.log(‘tail output: ‘ + data);

});

每當子進程將數(shù)據(jù)輸出到標準輸出stdout時,父進程就會得到通知并把數(shù)據(jù)打印到控制臺。

除了標準輸出,進程還有另外一個默認輸出流:標準錯誤流,通常用這個流來輸出錯誤信息。

在這個例子里,如果/var/log/system.log文件不存在,tail進程將會輸出類似下面的消息:“/var/log/system.log:No such file or directory”,通過監(jiān)聽stderr流,父進程會在這種錯誤發(fā)生時得到通知。

父進程可以這樣監(jiān)聽標準錯誤流:

復制代碼 代碼如下:

child.stderr.on('data', function(data) {

    console.log('tail error output:', data);

});

 stderr屬性和stdout一樣,也是只讀流,每當子進程往標準錯誤流里輸出數(shù)據(jù)時,父進程就會得到通知,并輸出數(shù)據(jù)。

發(fā)送數(shù)據(jù)到子進程

除了從子進程的輸出流里接收數(shù)據(jù),父進程還可以通過childPoces.stdin屬性往子進程的標準輸入里寫入數(shù)據(jù),以此來往子進程發(fā)送數(shù)據(jù)。

子進程可以通過process.stdin只讀流來監(jiān)聽標準輸入的數(shù)據(jù),但是注意你首先必須得恢復(resume)標準輸入流,因為它默認處于暫停(paused)狀態(tài)。

例8-6將會創(chuàng)建一個包含如下功能的程序:

1.+1 應用:一個簡單的應用程序,可以從標準輸入接收整型,然后相加,再把相加以后的結(jié)果輸出到標準輸出流。這個應用作為一個簡單的計算服務, 把Node進程模擬成一個可以執(zhí)行特定工作的外部服務。

2.測試+1應用的客戶端,發(fā)送隨機整型,然后輸出結(jié)果。用來演示Node進程如何生成一個子進程然后讓它執(zhí)行特定的任務。

用下面例8-6的代碼創(chuàng)建一個名為plus_one.js的文件:

例 8-6: +1 應用程序(chapter8/06_plus_one.js)

復制代碼 代碼如下:

// 恢復默認是暫停狀態(tài)的標準輸入流
process.stdin.resume();
process.stdin.on('data', function(data) {
    var number;
    try {
        // 將輸入數(shù)據(jù)解析為整型
        number = parseInt(data.toString(), 10);
        // +1
        number += 1;
        // 輸出結(jié)果
        process.stdout.write(number + "\n");
    } catch(err) {
        process.stderr.write(err.message + "\n");
    }
});

上面代碼里,我們等待來自stdin標準輸入流的數(shù)據(jù),每當有數(shù)據(jù)可用,就假設它是個整型并把它解析到一個整型變量里,然后加1,并把結(jié)果輸出到標準輸出流。

可以通過下面命令來運行這個程序:

復制代碼 代碼如下:

    $ node plus_one.js

運行后程序就開始等待輸入,如果你輸入一個整數(shù)然后按回車,就會看到一個被加1以后的數(shù)字被顯示到屏幕上。

可以通過按Ctrl-C來退出程序。

一個測試客戶端

現(xiàn)在你要創(chuàng)建一個Node進程來使用前面的“+1應用”提供的計算服務。

首先創(chuàng)建一個名為plus_one_test.js的文件,內(nèi)容見例8-7:

例 8-7: 測試+1應用(chapter8/07_plus_one_test.js)

復制代碼 代碼如下:

var spawn = require('child_process').spawn;
// 生成一個子進程來執(zhí)行+1應用
var child = spawn('node', ['plus_one.js']);
// 每一秒調(diào)用一次函數(shù)
setInterval(function() {
    // Create a random number smaller than 10.000
    var number = Math.floor(Math.random() * 10000);
    // Send that number to the child process:
    child.stdin.write(number + "\n");
    // Get the response from the child process and print it:
    child.stdout.once('data', function(data) {
        console.log('child replied to ' + number + ' with: ' + data);
    });
}, 1000);
child.stderr.on('data', function(data) {
    process.stdout.write(data);
});

 從第一行到第四行啟動了一個用來運行“+1應用”的子進程,然后使用setInterval函數(shù)每秒鐘執(zhí)行一次下列操作:

1..新建一個小于10000的隨機數(shù)
2.將這個數(shù)字作為字符串傳遞給子進程
3.等待子進程回復一個字符串
4.因為你想每次只接收1個數(shù)字的計算結(jié)果,因此需要使用child.stdout.once而不是child.stdout.on。如果使用了后者,會每隔1秒注冊一個data事件的回調(diào)函數(shù),每個被注冊的回調(diào)函數(shù)都會在子進程的stdout接收到數(shù)據(jù)時被執(zhí)行,這樣你會發(fā)現(xiàn)同一個計算結(jié)果會被輸出多次,這種行為顯然是錯的。

在子進程退出時接收通知

當子進程退出時,exit事件會被觸發(fā)。例8-8展示了如何監(jiān)聽它:

例 8-8: 監(jiān)聽子進程的退出事件 (chapter8/09_listen_child_exit.js)

復制代碼 代碼如下:

var spawn = require('child_process').spawn;
// 生成子進程來執(zhí)行 "ls -la"命令
var child = spawn('ls', ['-la']);
child.stdout.on('data', function(data) {
    console.log('data from child: ' + data);
});

// 當子進程退出:
<strong>child.on('exit', function(code) {
    console.log('child process terminated with code ' + code);
});</strong>

最后幾行加黑的代碼,父進程使用子進程的exit事件來監(jiān)聽它的退出事件,當事件發(fā)生時,控制臺顯示相應的輸出。子進程的退出碼會被作為第一個參數(shù)傳遞給回調(diào)函數(shù)。有些程序使用一個非0的退出碼來代表某種失敗狀態(tài)。比如,如果你嘗試執(zhí)行命令“l(fā)s –al click filename.txt”,但是當前目錄沒有這個文件,你就會得到一個值為1的退出碼,見例8-9:

例8-9:獲得子進程的退出碼 (chapter8/10_child_exit_code.js)

復制代碼 代碼如下:

var spawn = require('child_process').spawn;
// 生成子進程,執(zhí)行"ls does_not_exist.txt" 命令
var child = spawn('ls', ['does_not_exist.txt']);
// 當子進程退出
child.on('exit', function(code) {
    console.log('child process terminated with code ' + code);
});

這個例子里,exit事件觸發(fā)了回調(diào)函數(shù),并把子進程的退出碼作為第一個參數(shù)傳遞給它。如果子進程是被信號殺死而導致的非正常退出,那么相應的信號代碼會被當作第二個參數(shù)傳遞給回調(diào)函數(shù),如例8-10:

LISTING 8-10: 獲得子進程的退出信號(chapter8/11_child_exit_signal.js)

復制代碼 代碼如下:

var spawn = require('child_process').spawn;
// 生成子進程,運行"sleep 10"命令
var child = spawn('sleep', ['10']);
setTimeout(function() {
    child.kill();
}, 1000);
child.on('exit', function(code, signal) {
    if (code) {
        console.log('child process terminated with code ' + code);
    } else if (signal) {
        console.log('child process terminated because of signal ' + signal);
    }
});

這個例子里,啟動一個子進程來執(zhí)行sleep 10秒的操作,但是還沒到10秒就發(fā)送了一個SIGKILL信號給子進程,這將會導致如下的輸出:

復制代碼 代碼如下:

child process terminated because of signal SIGTERM

發(fā)送信號并殺死進程

在這部分,你將學習如何使用信號來管理子進程。信號是父進程用來跟子進程通信,甚至殺死子進程的一種簡單方式。

不同的信號代碼代表不同的含義,有很多信號,其中最常見的一些是用來殺死進程的。如果一個進程接收到一個它不知道如何處理的信號,程序就會被異常中斷。有些信號會被子進程處理,而有些只能由操作系統(tǒng)處理。

一般情況下,你可以使用child.kill方法來向子進程發(fā)送一個信號,默認發(fā)送SIGTERM信號:

復制代碼 代碼如下:

var spawn = require('child_process').spawn;
var child = spawn('sleep', ['10']);
setTimeout(function() {
    child.kill();
}, 1000);

還可以通過傳入一個標識信號的字符串作為kill方法的唯一參數(shù),來發(fā)送某個特定的信號:

復制代碼 代碼如下:

child.kill(‘SIGUSR2');

需要注意的是,雖然這個方法的名字叫kill,但是發(fā)送的信號并不一定會殺死子進程。如果子進程處理了信號,默認的信號行為就會被覆蓋。用Node寫的子進程可以像下面這樣重寫信號處理器的定義:

復制代碼 代碼如下:

process.on('SIGUSR2', function() {
    console.log('Got   a SIGUSR2 signal');
});

現(xiàn)在,你定義了SIGUSR2的信號處理器,當你的進程再收到SIGUSR2信號的時候就不會被殺死,而是輸出“Got a SIGUSR2 signal”這句話。使用這種機制,你可以設計一種簡單的方式來跟子進程溝通甚至命令它。雖然不像使用標準輸入功能那么豐富,但是這方式要簡單很多。

小結(jié)

這一章,學習了使用child_process.exec方法來執(zhí)行外部命令,這種方式可以不使用命令行參數(shù),而是通過定義環(huán)境變量的方式把參數(shù)傳遞給子進程。

還學習了通過調(diào)用child_process.spawn方法生成子進程的方式來調(diào)用外部命令,這種方式你可以使用輸入流,輸出流來跟子進程通信,或者使用信號來跟子進程通信以及殺死進程。

相關文章

  • 安裝nvm并使用nvm安裝nodejs及配置環(huán)境變量的全過程

    安裝nvm并使用nvm安裝nodejs及配置環(huán)境變量的全過程

    有時候使用nvm管理node會發(fā)現(xiàn)無法使用node或npm,主要原因是環(huán)境變量沒有配置成功,下面這篇文章主要給大家介紹了關于安裝nvm并使用nvm安裝nodejs及配置環(huán)境變量的相關資料,需要的朋友可以參考下
    2023-03-03
  • NodeJS學習筆記之Connect中間件應用實例

    NodeJS學習筆記之Connect中間件應用實例

    前面我們介紹了幾篇內(nèi)容的connect中間件的基礎知識,今天我們來實例應用一下,做個記事本的小應用,希望大家能夠喜歡。
    2015-01-01
  • node.js中的emitter.emit方法使用說明

    node.js中的emitter.emit方法使用說明

    這篇文章主要介紹了node.js中的emitter.emit方法使用說明,本文介紹了emitter.emit的方法說明、語法、接收參數(shù)、使用實例和實現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • NodeJs安裝npm包一直失敗的解決方法

    NodeJs安裝npm包一直失敗的解決方法

    本篇文章主要介紹了NodeJs安裝npm包一直失敗的解決方法。具有很好的參考價值。下面跟著小編一起來看下吧
    2017-04-04
  • Egret引擎開發(fā)指南之編譯項目

    Egret引擎開發(fā)指南之編譯項目

    Egret框架是一個基于MIT開源協(xié)議許可的永久免費的項目!你可以在項目中隨意使用且修改它,并且擁有100%的控制權。你可以從Egret的Github網(wǎng)站獲取它的源代碼,從而了解和學習它的核心細節(jié)。Egret具有完善的文檔,并且易于上手學習,可以讓你更容易專注于游戲本身的開發(fā)
    2014-09-09
  • Node.JS在命令行中檢查Chrome瀏覽器是否安裝并打開指定網(wǎng)址

    Node.JS在命令行中檢查Chrome瀏覽器是否安裝并打開指定網(wǎng)址

    這篇文章主要介紹了Node.JS在命令行中檢查Chrome瀏覽器是否安裝,并打開指定網(wǎng)址,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-05-05
  • 詳解如何利用Nodejs構(gòu)建多進程應用

    詳解如何利用Nodejs構(gòu)建多進程應用

    這篇文章主要為大家介紹了如何利用Nodejs構(gòu)建多進程應用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • 用npm install時報錯node-sass npm ERR command failed問題的解決方法

    用npm install時報錯node-sass npm ERR command

    在用npm install時報錯npm ERR! path D:…\node-sass和npm ERR! command failed 問題,本文給大家介紹了如何解決這個問題,文中通過圖文給大家介紹的非常詳細,需要的朋友可以參考下
    2024-03-03
  • mongoose設置unique不生效問題的解決及如何移除unique的限制

    mongoose設置unique不生效問題的解決及如何移除unique的限制

    這篇文章主要給大家介紹了關于mongoose數(shù)據(jù)庫設置unique不生效問題的解決方法,以及Mongoose如何移除unique限制的方法示例,文中通過示例代碼介紹的非常詳細,需要的朋友們可以參考借鑒,下面隨著小編來一起學習學習吧。
    2017-11-11
  • 使用node+vue.js實現(xiàn)SPA應用

    使用node+vue.js實現(xiàn)SPA應用

    這篇文章主要介紹了使用node+vue.js實現(xiàn)SPA應用的相關資料,需要的朋友可以參考下
    2016-01-01

最新評論