深入理解Node.js中的進(jìn)程管理
前言
本文主要對 Node.js 中進(jìn)程管理相關(guān)的東西做一個(gè)簡單介紹,包括 process 對象、child_process 模塊和cluster 模塊,詳細(xì)的 API 可以查看官方文檔,下面來看看詳細(xì)的介紹吧。
Process 對象
process 是 Node.js 的一個(gè)全局對象,可以在任何地方直接使用而不需要 require 命令加載。process 對象提供了 當(dāng)前 node 進(jìn)程 的命令行參數(shù)、標(biāo)準(zhǔn)輸入輸出、運(yùn)行環(huán)境和運(yùn)行狀態(tài)等信息。
常用屬性
argv
process.argv
屬性返回一個(gè)數(shù)組,第一個(gè)元素是 node,第二個(gè)元素是腳本文件名稱,其余成員是腳本文件的參數(shù)。
$ node process-2.js one two=three four 0: /usr/local/bin/node 1: /Users/mjr/work/node/process-2.js 2: one 3: two=three 4: four
env
process.env 返回一個(gè)對象,包含了當(dāng)前 Shell 的所有環(huán)境變量,比如:
{ TERM: 'xterm-256color', SHELL: '/bin/zsh', USER: 'huangtengfei', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/huangtengfei', HOME: '/Users/huangtengfei' }
這個(gè)屬性通常的使用場景是,新建一個(gè) NODE_ENV 變量,用來確定當(dāng)前所處的開發(fā)階段,生成階段設(shè)為 production,開發(fā)階段設(shè)為 develop ,然后在腳本中讀取 process.env.NODE_ENV
再做相應(yīng)處理即可。
運(yùn)行腳本時(shí)可以這樣改變環(huán)境變量:
$ export NODE_ENV=production && node app.js # 或者 $ NODE_ENV=production node app.js
stdin/stdout
process.stdin
指向標(biāo)準(zhǔn)輸入(鍵盤到緩沖區(qū)里的東西),返回一個(gè)可讀的流:
process.stdin.setEncoding('utf8'); process.stdin.on('readable', () => { var chunk = process.stdin.read(); if (chunk !== null) { process.stdout.write(`data: ${chunk}`); } }); process.stdin.on('end', () => { process.stdout.write('end'); });
process.stdout 指向標(biāo)準(zhǔn)輸出(向用戶顯示內(nèi)容),返回一個(gè)可寫的流:
const fs = require('fs'); fs.createReadStream('wow.txt') .pipe(process.stdout);
常用方法
cwd()
process.cwd()
返回運(yùn)行 Node 的工作目錄(絕對路徑),比如在目錄 /Users/huangtengfei/abc 下執(zhí)行 node server.js,那么 process.cwd()
返回的就是 /Users/huangtengfei/abc。
另一個(gè)常用的獲取路徑的方法是 __dirname,它返回的是執(zhí)行文件時(shí)該文件在文件系統(tǒng)中所在的目錄。注意 process.cwd()
和 __dirname
的不同,前者是進(jìn)程發(fā)起時(shí)的位置,后者是腳本的位置,兩者可能不一致。
on()
process 對象部署了 EventEmitter 接口,可以使用 process.on()
方法監(jiān)聽各種事件,并指定回調(diào)函數(shù)。比如監(jiān)聽到系統(tǒng)發(fā)出進(jìn)程終止信號時(shí)關(guān)閉服務(wù)器然后退出進(jìn)程:
process.on('SIGTERM', function () { server.close(function () { process.exit(0); }); });
exit()
process.exit()
會(huì)讓 Node 立即終止當(dāng)前進(jìn)程(同步),參數(shù)為一個(gè)退出狀態(tài)碼,0 表示成功,大于 0 的任意整數(shù)表示失敗。
kill()
process.kill()
用來對特定 id 的進(jìn)程(process.pid
)發(fā)送信號,默認(rèn)為 SIGINT 信號。比如殺死當(dāng)前進(jìn)程:
process.kill(process.pid, 'SIGTERM');
雖然名字叫 kill ,但其實(shí) process.kill()
只是負(fù)責(zé)發(fā)送信號,具體發(fā)送完信號之后這個(gè)怎么處理這個(gè)指定進(jìn)程,取決于信號種類和接收到這個(gè)信號之后做了什么操作(比如 process.exit()
或者只是 console.log('Ignored this single')
)。
Child Process 模塊
child_process 模塊用于創(chuàng)建和控制子進(jìn)程,其中最核心的是 .spawn()
,其他 API 算是針對特定場景對它的封裝。使用前要先 require 進(jìn)來:
const cp = require('child_process');
exec(command[, options][, callback])
exec()
方法用于執(zhí)行 shell 命令,它的第一個(gè)參數(shù)是字符串形式的命令,第二個(gè)參數(shù)(可選)用來指定子進(jìn)程運(yùn)行時(shí)的定制化操作,第三個(gè)參數(shù)(可選)用來設(shè)置執(zhí)行完命令的回調(diào)函數(shù)。比如在一個(gè)特定目錄 /Users/huangtengfei/abc 下執(zhí)行 ls -l
命令:
cp.exec('ls -l', { cwd: '/Users/huangtengfei/abc' }, (error, stdout, stderr) => { if (error) { console.error(`exec error: ${error}`); return; } console.log(`stdout: ${stdout}`); console.log(`stderr: ${stderr}`); })
spawn(command[, args][, options])
spawn()
用來創(chuàng)建一個(gè)子進(jìn)程執(zhí)行特定命令,與 exec()
的區(qū)別是它沒有回調(diào)函數(shù),只能通過監(jiān)聽事件來獲取運(yùn)行結(jié)果,它適用于子進(jìn)程長時(shí)間運(yùn)行的情況,可以實(shí)時(shí)輸出結(jié)果。
const ls = cp.spawn('ls', ['-l']); ls.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); ls.stderr.on('data', (data) => { console.log(`stderr: ${data}`); }); ls.on('close', (code) => { console.log(`child process exited with code $[code]`); });
使用 spawn 可以實(shí)現(xiàn)一個(gè)簡單的守護(hù)進(jìn)程,在工作進(jìn)程不正常退出時(shí)重啟工作進(jìn)程:
/* daemon.js */ function spawn(mainModule) { const worker = cp.spawn('node', [ mainModule ]); worker.on('exit', function (code) { if (code !== 0) { spawn(mainModule); } }); } spawn('worker.js');
fork(modulePath[, args][, options])
fork()
用來創(chuàng)建一個(gè)子進(jìn)程執(zhí)行 node 腳本,fork('./child.js')
相當(dāng)于 spawn('node', ['./child.js'])
, 區(qū)別在于 fork 會(huì)在父子進(jìn)程之間建立一個(gè)通信管道(fork() 的返回值),用于進(jìn)程間通信。對該通信管道對象可以監(jiān)聽 message 事件,用來獲取子進(jìn)程返回的信息,也可以向子進(jìn)程發(fā)送信息。
/* main.js */ const proc = cp.fork('./child.js'); proc.on('message', function(msg) { console.log(`parent got message: ${msg}`); }); proc.send({ hello: 'world' }); /* child.js */ process.on('message', function(msg) { console.log(`child got message: ${msg}`); }); process.send({ foo: 'bar' });
Cluster 模塊
Node.js 默認(rèn)單進(jìn)程執(zhí)行,但這樣就無法利用多核計(jì)算機(jī)的資源,cluster 模塊的出現(xiàn)就是為了解決這個(gè)問題的。在開發(fā)服務(wù)器程序時(shí),可以通過 cluster 創(chuàng)建一個(gè)主進(jìn)程和多個(gè) worker 進(jìn)程,讓每個(gè) worker 進(jìn)程運(yùn)行在一個(gè)核上,統(tǒng)一通過主進(jìn)程監(jiān)聽端口和分發(fā)請求。
const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { console.log(`Master ${process.pid} is running`); // Fork workers. for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`worker ${worker.process.pid} died`); }); } else { // Workers can share any TCP connection // In this case it is an HTTP server http.createServer((req, res) => { res.writeHead(200); res.end('hello world\n'); }).listen(8000); console.log(`Worker ${process.pid} started`); }
常用屬性和方法
isMaster/isWorker
cluster.isMaster
用來判斷當(dāng)前進(jìn)程是否是主進(jìn)程,cluster.isWorker
用來判斷當(dāng)前進(jìn)程是否是工作進(jìn)程,兩者返回的都是布爾值。
workers
cluster.workers
是一個(gè)包含所有 worker 進(jìn)程的對象,key 為 worker.id
,value 為 worker 進(jìn)程對象。
// 遍歷所有 workers function eachWorker(callback) { for (const id in cluster.workers) { callback(cluster.workers[id]); } } eachWorker((worker) => { worker.send('big announcement to all workers'); });
fork([env])
cluster.fork()
方法用來新建一個(gè) worker 進(jìn)程,默認(rèn)上下文復(fù)制主進(jìn)程,只有主進(jìn)程可調(diào)用。
常用事件
listening
在工作進(jìn)程調(diào)用 listen 方法后,會(huì)觸發(fā)一個(gè) listening 事件,這個(gè)事件可以被 cluster.on('listening')
監(jiān)聽。
比如每當(dāng)一個(gè) worker 進(jìn)程連進(jìn)來時(shí),輸出一條 log 信息:
cluster.on('listening', (worker, address) => { console.log( `A worker is now connected to ${address.address}:${address.port}`); });
exit
在工作進(jìn)程掛掉時(shí),會(huì)觸發(fā)一個(gè) exit 事件,這個(gè)事件可以被 cluster.on('exit')
監(jiān)聽。
比如自動(dòng)重啟 worker:
cluster.on('exit', (worker, code, signal) => { console.log('worker %d died (%s). restarting...', worker.process.pid, signal || code); cluster.fork(); });
worker 對象
worker 對象是 cluster.fork()
的返回值,代表一個(gè) worker 進(jìn)程。
worker.id
worker.id
是當(dāng)前 worker 的唯一標(biāo)識,也是保存在 cluster.workers
中的 key 值。
worker.process
所有的 worker 進(jìn)程都是通過 child_process.fork()
生成的,這個(gè)進(jìn)程對象保存在 worker.process
中。
worker.send()
worker.send()
用在主進(jìn)程給子進(jìn)程發(fā)送消息,在子進(jìn)程中,使用 process.on()
監(jiān)聽消息并使用 process.send()
發(fā)送消息。
if (cluster.isMaster) { const worker = cluster.fork(); worker.send('hi there'); } else if (cluster.isWorker) { process.on('message', (msg) => { process.send(msg); }); }
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
玩轉(zhuǎn)NODE.JS(四)-搭建簡單的聊天室的代碼
本篇文章主要介紹了利用NODE.JS搭建簡單的聊天室的代碼,有需要的可以了解一下。2016-11-11詳解阿里Node.js技術(shù)文檔之process模塊學(xué)習(xí)指南
這篇文章主要介紹了詳解阿里Node.js技術(shù)文檔之process模塊學(xué)習(xí)指南,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01windows使用nvm對node進(jìn)行版本管理切換的完整步驟
這篇文章主要介紹了windows使用nvm對node進(jìn)行版本管理切換的完整步驟,在使用之前各位務(wù)必卸載掉自己安裝過的nvm或者node版本包括環(huán)境變量之類的,要保證自己的電腦完全沒有node環(huán)境,需要的朋友可以參考下2024-03-03快速掌握Node.js環(huán)境的安裝與運(yùn)行方法
這篇文章主要介紹了Node.js環(huán)境的安裝與運(yùn)行方法,Node是基于Google Chrome V8引擎的JavaScript解釋器,需要的朋友可以參考下2016-02-02Node.js中,在cmd界面,進(jìn)入退出Node.js運(yùn)行環(huán)境的方法
今天小編就為大家分享一篇Node.js中,在cmd界面,進(jìn)入退出Node.js運(yùn)行環(huán)境的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05