進(jìn)程與線程在服務(wù)端研發(fā)中是一個(gè)非常重要的概念,如果您在學(xué)習(xí)的時(shí)候?qū)@一塊感到混亂或者不是太理解,可以閱讀下本篇內(nèi)容,本篇在介紹進(jìn)程和線程的概念之外,列舉了很多 Demo 希望能從實(shí)戰(zhàn)角度幫助您更好的去理解。
作者簡(jiǎn)介:五月君,Nodejs Developer,熱愛技術(shù)、喜歡分享的 90 后青年,公眾號(hào) “Nodejs技術(shù)?!保珿ithub 開源項(xiàng)目 https://www.nodejs.red
快速導(dǎo)航
進(jìn)程
進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ),進(jìn)程是線程的容器(來自百科)。我們啟動(dòng)一個(gè)服務(wù)、運(yùn)行一個(gè)實(shí)例,就是開一個(gè)服務(wù)進(jìn)程,例如 Java 里的 JVM 本身就是一個(gè)進(jìn)程,Node.js 里通過 node app.js 開啟一個(gè)服務(wù)進(jìn)程,多進(jìn)程就是進(jìn)程的復(fù)制(fork),fork 出來的每個(gè)進(jìn)程都擁有自己的獨(dú)立空間地址、數(shù)據(jù)棧,一個(gè)進(jìn)程無法訪問另外一個(gè)進(jìn)程里定義的變量、數(shù)據(jù)結(jié)構(gòu),只有建立了 IPC 通信,進(jìn)程之間才可數(shù)據(jù)共享。
關(guān)于進(jìn)程通過一個(gè)簡(jiǎn)單的 Node.js Demo 來驗(yàn)證下,執(zhí)行以下代碼
node process.js,開啟一個(gè)服務(wù)進(jìn)程
// process.js
const http = require('http');
http.createServer().listen(3000, () => {
process.title = '測(cè)試進(jìn)程 Node.js' // 進(jìn)程進(jìn)行命名
console.log(`process.pid: `, process.pid); // process.pid: 20279
});
以下為 Mac 系統(tǒng)自帶的監(jiān)控工具 “活動(dòng)監(jiān)視器” 所展示的效果,可以看到我們剛開啟的 Nodejs 進(jìn)程 20279
線程
線程是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位,首先我們要清楚線程是隸屬于進(jìn)程的,被包含于進(jìn)程之中。一個(gè)線程只能隸屬于一個(gè)進(jìn)程,但是一個(gè)進(jìn)程是可以擁有多個(gè)線程的。
同一塊代碼,可以根據(jù)系統(tǒng)CPU核心數(shù)啟動(dòng)多個(gè)進(jìn)程,每個(gè)進(jìn)程都有屬于自己的獨(dú)立運(yùn)行空間,進(jìn)程之間是不相互影響的。同一進(jìn)程中的多條線程將共享該進(jìn)程中的全部系統(tǒng)資源,如虛擬地址空間,文件描述符和信號(hào)處理等。但同一進(jìn)程中的多個(gè)線程有各自的調(diào)用棧(call stack),自己的寄存器環(huán)境(register context),自己的線程本地存儲(chǔ)(thread-local storage),線程又有單線程和多線程之分,具有代表性的 JavaScript、Java 語言。
單線程
單線程就是一個(gè)進(jìn)程只開一個(gè)線程,想象一下一個(gè)癡情的少年,對(duì)一個(gè)妹子一心一意用情專一。
Javascript 就是屬于單線程,程序順序執(zhí)行,可以想象一下隊(duì)列,前面一個(gè)執(zhí)行完之后,后面才可以執(zhí)行,當(dāng)你在使用單線程語言編碼時(shí)切勿有過多耗時(shí)的同步操作,否則線程會(huì)造成阻塞,導(dǎo)致后續(xù)響應(yīng)無法處理。你如果采用 Javascript 進(jìn)行編碼時(shí)候,請(qǐng)盡可能的使用異步操作。
一個(gè)計(jì)算耗時(shí)造成線程阻塞的例子
先看一段例子,運(yùn)行下面程序,瀏覽器執(zhí)行 http://127.0.0.1:3000/compute 大約每次需要 15657.310ms,也就意味下次用戶請(qǐng)求需要等待 15657.310ms,下文 Node.js 進(jìn)程創(chuàng)建一節(jié) 將采用 child_process.fork 實(shí)現(xiàn)多個(gè)進(jìn)程來處理。
// compute.js
const http = require('http');
const [url, port] = ['127.0.0.1', 3000];
const computation = () => {
let sum = 0;
console.info('計(jì)算開始');
console.time('計(jì)算耗時(shí)');
for (let i = 0; i < 1e10; i++) {
sum += i
};
console.info('計(jì)算結(jié)束');
console.timeEnd('計(jì)算耗時(shí)');
return sum;
};
const server = http.createServer((req, res) => {
if(req.url == '/compute'){
const sum = computation();
res.end(`Sum is ${sum}`);
}
res.end(`ok`);
});
server.listen(port, url, () => {
console.log(`server started at http://${url}:${port}`);
});
單線程使用總結(jié)
- Node.js 雖然是單線程模型,但是其基于事件驅(qū)動(dòng)、異步非阻塞模式,可以應(yīng)用于高并發(fā)場(chǎng)景,避免了線程創(chuàng)建、線程之間上下文切換所產(chǎn)生的資源開銷。
- 如果你有需要大量計(jì)算,CPU 耗時(shí)的操作,開發(fā)時(shí)候要注意。
多線程
多線程就是沒有一個(gè)進(jìn)程只開一個(gè)線程的限制,好比一個(gè)風(fēng)流少年除了愛慕自己班的某個(gè)妹子,還在想著隔壁班的漂亮妹子。Java 就是多線程編程語言的一種,可以有效避免代碼阻塞導(dǎo)致的后續(xù)請(qǐng)求無法處理。
對(duì)于多線程的說明 Java 是一個(gè)很好的例子,看以下代碼示例,我將 count 定義在全局變量,如果定義在 test 方法里,又會(huì)輸出什么呢?
public class TestApplication {
Integer count = 0;
@GetMapping("/test")
public Integer Test() {
count += 1;
return count;
}
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
運(yùn)行結(jié)果,每次執(zhí)行都會(huì)修改count值,所以,多線程中任何一個(gè)變量都可以被任何一個(gè)線程所修改。
1 # 第一次執(zhí)行
2 # 第二次執(zhí)行
3 # 第三次執(zhí)行
我現(xiàn)在對(duì)上述代碼做下修改將 count 定義在 test 方法里
public class TestApplication {
@GetMapping("/test")
public Integer Test() {
Integer count = 0; // 改變定義位置
count += 1;
return count;
}
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
運(yùn)行結(jié)果如下所示,每次都是 1,因?yàn)槊總€(gè)線程都擁有了自己的執(zhí)行棧
1 # 第一次執(zhí)行
1 # 第二次執(zhí)行
1 # 第三次執(zhí)行
多線程使用總結(jié)
多線程的代價(jià)還在于創(chuàng)建新的線程和執(zhí)行期上下文線程的切換開銷,由于每創(chuàng)建一個(gè)線程就會(huì)占用一定的內(nèi)存,當(dāng)應(yīng)用程序并發(fā)大了之后,內(nèi)存將會(huì)很快耗盡。類似于上面單線程模型中例舉的例子,需要一定的計(jì)算會(huì)造成當(dāng)前線程阻塞的,還是推薦使用多線程來處理,關(guān)于線程與進(jìn)程的理解推薦閱讀下 阮一峰:進(jìn)程與線程的一個(gè)簡(jiǎn)單解釋。
Nodejs的線程與進(jìn)程
Node.js 是 Javascript 在服務(wù)端的運(yùn)行環(huán)境,構(gòu)建在 chrome 的 V8 引擎之上,基于事件驅(qū)動(dòng)、非阻塞I/O模型,充分利用操作系統(tǒng)提供的異步 I/O 進(jìn)行多任務(wù)的執(zhí)行,適合于 I/O 密集型的應(yīng)用場(chǎng)景,因?yàn)楫惒?,程序無需阻塞等待結(jié)果返回,而是基于回調(diào)通知的機(jī)制,原本同步模式等待的時(shí)間,則可以用來處理其它任務(wù),在 Web 服務(wù)器方面,著名的 Nginx 也是采用此模式(事件驅(qū)動(dòng)),Nginx 采用 C 語言進(jìn)行編寫,主要用來做高性能的 Web 服務(wù)器,不適合做業(yè)務(wù)。Web業(yè)務(wù)開發(fā)中,如果你有高并發(fā)應(yīng)用場(chǎng)景那么 Node.js 會(huì)是你不錯(cuò)的選擇。
在單核 CPU 系統(tǒng)之上我們采用 單進(jìn)程 + 單線程 的模式來開發(fā)。在多核 CPU 系統(tǒng)之上,可以通過 child_process.fork 開啟多個(gè)進(jìn)程(Node.js 在 v0.8 版本之后新增了Cluster 來實(shí)現(xiàn)多進(jìn)程架構(gòu)) ,即 多進(jìn)程 + 單線程 模式。注意:開啟多進(jìn)程不是為了解決高并發(fā),主要是解決了單進(jìn)程模式下 Node.js CPU 利用率不足的情況,充分利用多核 CPU 的性能。
Process
Node.js 中的進(jìn)程 Process 是一個(gè)全局對(duì)象,無需 require 直接使用,給我們提供了當(dāng)前進(jìn)程中的相關(guān)信息。官方文檔提供了詳細(xì)的說明,感興趣的可以親自實(shí)踐下 Process 文檔。
- process.env:環(huán)境變量,例如通過 process.env.NODE_ENV 獲取不同環(huán)境項(xiàng)目配置信息
- process.nextTick:這個(gè)在談及 Event Loop 時(shí)經(jīng)常為會(huì)提到
- process.pid:獲取當(dāng)前進(jìn)程id
- process.ppid:當(dāng)前進(jìn)程對(duì)應(yīng)的父進(jìn)程
- process.cwd():獲取當(dāng)前進(jìn)程工作目錄
- process.platform:獲取當(dāng)前進(jìn)程運(yùn)行的操作系統(tǒng)平臺(tái)
- process.uptime():當(dāng)前進(jìn)程已運(yùn)行時(shí)間,例如:pm2 守護(hù)進(jìn)程的 uptime 值
- 進(jìn)程事件:process.on('uncaughtException', cb) 捕獲異常信息、process.on('exit', cb)進(jìn)程推出監(jiān)聽
- 三個(gè)標(biāo)準(zhǔn)流:process.stdout 標(biāo)準(zhǔn)輸出、process.stdin 標(biāo)準(zhǔn)輸入、process.stderr 標(biāo)準(zhǔn)錯(cuò)誤輸出
以上僅列舉了部分常用到功能點(diǎn),除了 Process 之外 Node.js 還提供了 child_process 模塊用來對(duì)子進(jìn)程進(jìn)行操作,在下文 Nodejs進(jìn)程創(chuàng)建一節(jié) 會(huì)講述。
關(guān)于 Node.js 進(jìn)程的幾點(diǎn)總結(jié)
- Javascript 是單線程,但是做為宿主環(huán)境的 Node.js 并非是單線程的。
- 由于單線程原故,一些復(fù)雜的、消耗 CPU 資源的任務(wù)建議不要交給 Node.js 來處理,當(dāng)你的業(yè)務(wù)需要一些大量計(jì)算、視頻編碼解碼等 CPU 密集型的任務(wù),可以采用 C 語言。
- Node.js 和 Nginx 均采用事件驅(qū)動(dòng)方式,避免了多線程的線程創(chuàng)建、線程上下文切換的開銷。如果你的業(yè)務(wù)大多是基于 I/O 操作,那么你可以選擇 Node.js 來開發(fā)。
Nodejs進(jìn)程創(chuàng)建
Node.js 提供了 child_process 內(nèi)置模塊,用于創(chuàng)建子進(jìn)程,更多詳細(xì)信息可參考 Node.js 中文網(wǎng) child_process
四種方式
-
child_process.spawn():適用于返回大量數(shù)據(jù),例如圖像處理,二進(jìn)制數(shù)據(jù)處理。 -
child_process.exec():適用于小量數(shù)據(jù),maxBuffer 默認(rèn)值為 200 * 1024 超出這個(gè)默認(rèn)值將會(huì)導(dǎo)致程序崩潰,數(shù)據(jù)量過大可采用 spawn。 -
child_process.execFile():類似 child_process.exec(),區(qū)別是不能通過 shell 來執(zhí)行,不支持像 I/O 重定向和文件查找這樣的行為 -
child_process.fork(): 衍生新的進(jìn)程,進(jìn)程之間是相互獨(dú)立的,每個(gè)進(jìn)程都有自己的 V8 實(shí)例、內(nèi)存,系統(tǒng)資源是有限的,不建議衍生太多的子進(jìn)程出來,通長(zhǎng)根據(jù)系統(tǒng) CPU 核心數(shù)設(shè)置。
方式一:spawn
child_process.spawn(command[, args][, options])
創(chuàng)建父子進(jìn)程間通信的三種方式:
- 讓子進(jìn)程的stdio和當(dāng)前進(jìn)程的stdio之間建立管道鏈接
child.stdout.pipe(process.stdout); - 父進(jìn)程子進(jìn)程之間共用stdio
- 事件監(jiān)聽
const spawn = require('child_process').spawn;
const child = spawn('ls', ['-l'], { cwd: '/usr' }) // cwd 指定子進(jìn)程的工作目錄,默認(rèn)當(dāng)前目錄
child.stdout.pipe(process.stdout);
console.log(process.pid, child.pid); // 主進(jìn)程id3243 子進(jìn)程3244
方式二:exec
const exec = require('child_process').exec;
exec(`node -v`, (error, stdout, stderr) => {
console.log({ error, stdout, stderr })
// { error: null, stdout: 'v8.5.0\n', stderr: '' }
})
方式三:execFile
const execFile = require('child_process').execFile;
execFile(`node`, ['-v'], (error, stdout, stderr) => {
console.log({ error, stdout, stderr })
// { error: null, stdout: 'v8.5.0\n', stderr: '' }
})
方式四:fork
const fork = require('child_process').fork;
fork('./worker.js'); // fork 一個(gè)新的子進(jìn)程
fork子進(jìn)程充分利用CPU資源
上文單線程一節(jié) 例子中,當(dāng) CPU 計(jì)算密度大的情況程序會(huì)造成阻塞導(dǎo)致后續(xù)請(qǐng)求需要等待,下面采用 child_process.fork 方法,在進(jìn)行 cpmpute 計(jì)算時(shí)創(chuàng)建子進(jìn)程,子進(jìn)程計(jì)算完成通過 send 方法將結(jié)果發(fā)送給主進(jìn)程,主進(jìn)程通過 message 監(jiān)聽到信息后處理并退出。
fork_app.js
const http = require('http');
const fork = require('child_process').fork;
const server = http.createServer((req, res) => {
if(req.url == '/compute'){
const compute = fork('./fork_compute.js');
compute.send('開啟一個(gè)新的子進(jìn)程');
// 當(dāng)一個(gè)子進(jìn)程使用 process.send() 發(fā)送消息時(shí)會(huì)觸發(fā) 'message' 事件
compute.on('message', sum => {
res.end(`Sum is ${sum}`);
compute.kill();
});
// 子進(jìn)程監(jiān)聽到一些錯(cuò)誤消息退出
compute.on('close', (code, signal) => {
console.log(`收到close事件,子進(jìn)程收到信號(hào) ${signal} 而終止,退出碼 ${code}`);
compute.kill();
})
}else{
res.end(`ok`);
}
});
server.listen(3000, 127.0.0.1, () => {
console.log(`server started at http://${127.0.0.1}:${3000}`);
});
fork_compute.js
針對(duì) 上文單線程一節(jié) 的例子需要進(jìn)行計(jì)算的部分拆分出來單獨(dú)進(jìn)行運(yùn)算。
const computation = () => {
let sum = 0;
console.info('計(jì)算開始');
console.time('計(jì)算耗時(shí)');
for (let i = 0; i < 1e10; i++) {
sum += i
};
console.info('計(jì)算結(jié)束');
console.timeEnd('計(jì)算耗時(shí)');
return sum;
};
process.on('message', msg => {
console.log(msg, 'process.pid', process.pid); // 子進(jìn)程id
const sum = computation();
// 如果Node.js進(jìn)程是通過進(jìn)程間通信產(chǎn)生的,那么,process.send()方法可以用來給父進(jìn)程發(fā)送消息
process.send(sum);
})
Nodejs多進(jìn)程架構(gòu)模型
多進(jìn)程架構(gòu)解決了單進(jìn)程、單線程無法充分利用系統(tǒng)多核 CPU 的問題,通過上文對(duì) Node.js 進(jìn)程有了初步的了解,本節(jié)通過一個(gè) Demo 來展示如何啟動(dòng)一批 Node.js 進(jìn)程來提供服務(wù)。
編寫主進(jìn)程
master.js 主要處理以下邏輯:
- 創(chuàng)建一個(gè) server 并監(jiān)聽 3000 端口。
- 根據(jù)系統(tǒng) cpus 開啟多個(gè)子進(jìn)程
- 通過子進(jìn)程對(duì)象的 send 方法發(fā)送消息到子進(jìn)程進(jìn)行通信
- 在主進(jìn)程中監(jiān)聽了子進(jìn)程的變化,如果是自殺信號(hào)重新啟動(dòng)一個(gè)工作進(jìn)程。
- 主進(jìn)程在監(jiān)聽到退出消息的時(shí)候,先退出子進(jìn)程在退出主進(jìn)程
// master.js
const fork = require('child_process').fork;
const cpus = require('os').cpus();
const server = require('net').createServer();
server.listen(3000);
process.title = 'node-master'
const workers = {};
const createWorker = () => {
const worker = fork('worker.js')
worker.on('message', function (message) {
if (message.act === 'suicide') {
createWorker();
}
})
worker.on('exit', function(code, signal) {
console.log('worker process exited, code: %s signal: %s', code, signal);
delete workers[worker.pid];
});
worker.send('server', server);
workers[worker.pid] = worker;
console.log('worker process created, pid: %s ppid: %s', worker.pid, process.pid);
}
for (let i=0; i<cpus.length; i++) {
createWorker();
}
process.once('SIGINT', close.bind(this, 'SIGINT')); // kill(2) Ctrl-C
process.once('SIGQUIT', close.bind(this, 'SIGQUIT')); // kill(3) Ctrl-\
process.once('SIGTERM', close.bind(this, 'SIGTERM')); // kill(15) default
process.once('exit', close.bind(this));
function close (code) {
console.log('進(jìn)程退出!', code);
if (code !== 0) {
for (let pid in workers) {
console.log('master process exited, kill worker pid: ', pid);
workers[pid].kill('SIGINT');
}
}
process.exit(0);
}
工作進(jìn)程
worker.js 子進(jìn)程處理邏輯如下:
- 創(chuàng)建一個(gè) server 對(duì)象,注意這里最開始并沒有監(jiān)聽 3000 端口
- 通過 message 事件接收主進(jìn)程 send 方法發(fā)送的消息
- 監(jiān)聽 uncaughtException 事件,捕獲未處理的異常,發(fā)送自殺信息由主進(jìn)程重建進(jìn)程,子進(jìn)程在鏈接關(guān)閉之后退出
// worker.js
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/plan'
});
res.end('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid);
throw new Error('worker process exception!'); // 測(cè)試異常進(jìn)程退出、重建
});
let worker;
process.title = 'node-worker'
process.on('message', function (message, sendHandle) {
if (message === 'server') {
worker = sendHandle;
worker.on('connection', function(socket) {
server.emit('connection', socket);
});
}
});
process.on('uncaughtException', function (err) {
console.log(err);
process.send({act: 'suicide'});
worker.close(function () {
process.exit(1);
})
})
測(cè)試
控制臺(tái)執(zhí)行 node master.js 可以看到已成功創(chuàng)建了四個(gè)工作進(jìn)程
$ node master
worker process created, pid: 19280 ppid: 19279
worker process created, pid: 19281 ppid: 19279
worker process created, pid: 19282 ppid: 19279
worker process created, pid: 19283 ppid: 19279
打開活動(dòng)監(jiān)視器查看我們的進(jìn)程情況,由于在創(chuàng)建進(jìn)程時(shí)對(duì)進(jìn)程進(jìn)行了命名,很清楚的看到一個(gè)主進(jìn)程對(duì)應(yīng)多個(gè)子進(jìn)程。
守護(hù)進(jìn)程
關(guān)于守護(hù)進(jìn)程,是什么、為什么、怎么編寫?本節(jié)將解密這些疑點(diǎn)
守護(hù)進(jìn)程運(yùn)行在后臺(tái)不受終端的影響,什么意思呢?Node.js 開發(fā)的同學(xué)們可能熟悉,當(dāng)我們打開終端執(zhí)行 node app.js 開啟一個(gè)服務(wù)進(jìn)程之后,這個(gè)終端就會(huì)一直被占用,如果關(guān)掉終端,服務(wù)就會(huì)斷掉,即前臺(tái)運(yùn)行模式。如果采用守護(hù)進(jìn)程進(jìn)程方式,這個(gè)終端我執(zhí)行 node app.js 開啟一個(gè)服務(wù)進(jìn)程之后,我還可以在這個(gè)終端上做些別的事情,且不會(huì)相互影響。
創(chuàng)建步驟
- 創(chuàng)建子進(jìn)程
- 在子進(jìn)程中創(chuàng)建新會(huì)話(調(diào)用系統(tǒng)函數(shù) setsid)
- 改變子進(jìn)程工作目錄(如:“/” 或 “/usr/ 等)
- 父進(jìn)程終止
Node.js 編寫守護(hù)進(jìn)程 Demo 展示
index.js 文件里的處理邏輯使用 spawn 創(chuàng)建子進(jìn)程完成了上面的第一步操作。設(shè)置 options.detached 為 true 可以使子進(jìn)程在父進(jìn)程退出后繼續(xù)運(yùn)行(系統(tǒng)層會(huì)調(diào)用 setsid 方法),參考 options_detached,這是第二步操作。options.cwd 指定當(dāng)前子進(jìn)程工作目錄若不做設(shè)置默認(rèn)繼承當(dāng)前工作目錄,這是第三步操作。運(yùn)行 daemon.unref() 退出父進(jìn)程,參考 options.stdio,這是第四步操作。
// index.js
const spawn = require('child_process').spawn;
function startDaemon() {
const daemon = spawn('node', ['daemon.js'], {
cwd: '/usr',
detached : true,
stdio: 'ignore',
});
console.log('守護(hù)進(jìn)程開啟 父進(jìn)程 pid: %s, 守護(hù)進(jìn)程 pid: %s', process.pid, daemon.pid);
daemon.unref();
}
startDaemon()
daemon.js 文件里處理邏輯開啟一個(gè)定時(shí)器每 10 秒執(zhí)行一次,使得這個(gè)資源不會(huì)退出,同時(shí)寫入日志到子進(jìn)程當(dāng)前工作目錄下
// /usr/daemon.js
const fs = require('fs');
const { Console } = require('console');
// custom simple logger
const logger = new Console(fs.createWriteStream('./stdout.log'), fs.createWriteStream('./stderr.log'));
setInterval(function() {
logger.log('daemon pid: ', process.pid, ', ppid: ', process.ppid);
}, 1000 * 10);
守護(hù)進(jìn)程實(shí)現(xiàn) Node.js 版本 源碼地址
運(yùn)行測(cè)試
$ node index.js
守護(hù)進(jìn)程開啟 父進(jìn)程 pid: 47608, 守護(hù)進(jìn)程 pid: 47609
打開活動(dòng)監(jiān)視器查看,目前只有一個(gè)進(jìn)程 47609,這就是我們需要進(jìn)行守護(hù)的進(jìn)程
守護(hù)進(jìn)程閱讀推薦
守護(hù)進(jìn)程總結(jié)
在實(shí)際工作中對(duì)于守護(hù)進(jìn)程并不陌生,例如 PM2、Egg-Cluster 等,以上只是一個(gè)簡(jiǎn)單的 Demo 對(duì)守護(hù)進(jìn)程做了一個(gè)說明,在實(shí)際工作中對(duì)守護(hù)進(jìn)程的健壯性要求還是很高的,例如:進(jìn)程的異常監(jiān)聽、工作進(jìn)程管理調(diào)度、進(jìn)程掛掉之后重啟等等,這些還需要我們?nèi)ゲ粩嗨伎肌?/p>
關(guān)于本節(jié)內(nèi)容面試相關(guān)的推薦閱讀我的另一片篇文章 分享 10 道 Nodejs 進(jìn)程相關(guān)面試題