NodeJs 進程管理應用場景

2021-09-15 10:19 更新

和進程管理相關的API單獨介紹起來比較枯燥,因此這里從一些典型的應用場景出發(fā),分別介紹一些重要API的使用方法。

如何獲取命令行參數

在NodeJS中可以通過process.argv獲取命令行參數。但是比較意外的是,node執(zhí)行程序路徑和主模塊文件路徑固定占據了argv[0]argv[1]兩個位置,而第一個命令行參數從argv[2]開始。為了讓argv使用起來更加自然,可以按照以下方式處理。

function main(argv) {
    // ...
}

main(process.argv.slice(2));

如何退出程序

通常一個程序做完所有事情后就正常退出了,這時程序的退出狀態(tài)碼為0?;蛘咭粋€程序運行時發(fā)生了異常后就掛了,這時程序的退出狀態(tài)碼不等于0。如果我們在代碼中捕獲了某個異常,但是覺得程序不應該繼續(xù)運行下去,需要立即退出,并且需要把退出狀態(tài)碼設置為指定數字,比如1,就可以按照以下方式:

try {
    // ...
} catch (err) {
    // ...
    process.exit(1);
}

如何控制輸入輸出

NodeJS程序的標準輸入流(stdin)、一個標準輸出流(stdout)、一個標準錯誤流(stderr)分別對應process.stdin、process.stdoutprocess.stderr,第一個是只讀數據流,后邊兩個是只寫數據流,對它們的操作按照對數據流的操作方式即可。例如,console.log可以按照以下方式實現。

function log() {
    process.stdout.write(
        util.format.apply(util, arguments) + '\n');
}

如何降權

在Linux系統(tǒng)下,我們知道需要使用root權限才能監(jiān)聽1024以下端口。但是一旦完成端口監(jiān)聽后,繼續(xù)讓程序運行在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);
});

上例中有幾點需要注意:

  1. 如果是通過sudo獲取root權限的,運行程序的用戶的UID和GID保存在環(huán)境變量SUDO_UIDSUDO_GID里邊。如果是通過chmod +s方式獲取root權限的,運行程序的用戶的UID和GID可直接通過process.getuidprocess.getgid方法獲取。

  2. process.setuidprocess.setgid方法只接受number類型的參數。

  3. 降權時必須先降GID再降UID,否則順序反過來的話就沒權限更改程序的GID了。

如何創(chuàng)建子進程

以下是一個創(chuàng)建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)方法,該方法支持三個參數。第一個參數是執(zhí)行文件路徑,可以是執(zhí)行文件的相對或絕對路徑,也可以是根據PATH環(huán)境變量能找到的執(zhí)行文件名。第二個參數中,數組中的每個成員都按順序對應一個命令行參數。第三個參數可選,用于配置子進程的執(zhí)行環(huán)境與行為。

另外,上例中雖然通過子進程對象的.stdout.stderr訪問子進程的輸出,但通過options.stdio字段的不同配置,可以將子進程的輸入輸出重定向到任何數據流上,或者讓子進程共享父進程的標準輸入輸出流,或者直接忽略子進程的輸入輸出。

進程間如何通訊

在Linux系統(tǒng)下,進程之間可以通過信號互相通信。以下是一個例子。

/* parent.js */
var child = child_process.spawn('node', [ 'child.js' ]);

child.kill('SIGTERM');

/* child.js */
process.on('SIGTERM', function () {
    cleanUp();
    process.exit(0);
});

在上例中,父進程通過.kill方法向子進程發(fā)送SIGTERM信號,子進程監(jiān)聽process對象的SIGTERM事件響應信號。不要被.kill方法的名稱迷惑了,該方法本質上是用來給進程發(fā)送信號的,進程收到信號后具體要做啥,完全取決于信號的種類和進程自身的代碼。

另外,如果父子進程都是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);
});

可以看到,父進程在創(chuàng)建子進程時,在options.stdio字段中通過ipc開啟了一條IPC通道,之后就可以監(jiān)聽子進程對象的message事件接收來自子進程的消息,并通過.send方法給子進程發(fā)送消息。在子進程這邊,可以在process對象上監(jiān)聽message事件接收來自父進程的消息,并通過.send方法向父進程發(fā)送消息。數據在傳遞過程中,會先在發(fā)送端使用JSON.stringify方法序列化,再在接收端使用JSON.parse方法反序列化。

如何守護子進程

守護進程一般用于監(jiān)控工作進程的運行狀態(tài),在工作進程不正常退出時重啟工作進程,保障工作進程不間斷運行。以下是一種實現方式。

/* 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');

可以看到,工作進程非正常退出時,守護進程立即重啟工作進程。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號