聊聊各種可能導(dǎo)致 Node.js 進(jìn)程退出的情況_第1頁
聊聊各種可能導(dǎo)致 Node.js 進(jìn)程退出的情況_第2頁
聊聊各種可能導(dǎo)致 Node.js 進(jìn)程退出的情況_第3頁
聊聊各種可能導(dǎo)致 Node.js 進(jìn)程退出的情況_第4頁
聊聊各種可能導(dǎo)致 Node.js 進(jìn)程退出的情況_第5頁
已閱讀5頁,還剩2頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第聊聊各種可能導(dǎo)致Node.js進(jìn)程退出的情況實(shí)現(xiàn)效果如下,每次按下ctrl+c都會(huì)提示用戶:

進(jìn)程主動(dòng)退出

Node.js進(jìn)程主動(dòng)退出,主要包含下面幾種情況:

代碼執(zhí)行過程中觸發(fā)了未捕獲的錯(cuò)誤,可以通過process.on(uncaughtException)監(jiān)聽這種情況

代碼執(zhí)行過程中觸發(fā)了未處理的promiserejection(Node.jsv16開始會(huì)導(dǎo)致進(jìn)程退出),可以通過process.on(unhandledRejection)監(jiān)聽這種情況

EventEmitter觸發(fā)了未監(jiān)聽的error事件

代碼中主動(dòng)調(diào)用process.exit函數(shù)退出進(jìn)程,可以通過process.on(exit)監(jiān)聽

Node.js的事件隊(duì)列為空,可簡單認(rèn)為沒有需要執(zhí)行的代碼了,可以通過process.on(exit)監(jiān)聽

我們知道pm2有守護(hù)進(jìn)程的效果,在你的進(jìn)程發(fā)生錯(cuò)誤退出時(shí),pm2會(huì)重啟你的進(jìn)程,我們也在Node.js的cluster模式下,實(shí)現(xiàn)一個(gè)守護(hù)子進(jìn)程的效果(實(shí)際上pm2也是類似的邏輯):

constcluster=require(cluster

consthttp=require(http

constnumCPUs=require(os).cpus().length;

constprocess=require(process

//主進(jìn)程代碼

if(cluster.isMaster){

console.log(`啟動(dòng)主進(jìn)程:${process.pid}`);

//根據(jù)cpu核數(shù),創(chuàng)建工作進(jìn)程

for(leti=0;inumCPUs;i++){

cluster.fork();

//監(jiān)聽工作進(jìn)程退出事件

cluster.on(exit,(worker,code,signal)={

console.log(`工作進(jìn)程${cess.pid}退出,錯(cuò)誤碼:${code||signal},重啟中...`);

//重啟子進(jìn)程

cluster.fork();

//工作進(jìn)程代碼

if(cluster.isWorker){

//監(jiān)聽未捕獲錯(cuò)誤事件

process.on(uncaughtException,error={

console.log(`工作進(jìn)程${process.pid}發(fā)生錯(cuò)誤`,error);

process.emit(disconnect

process.exit(1);

//創(chuàng)建webserver

//各個(gè)工作進(jìn)程都會(huì)監(jiān)聽端口8000(Node.js內(nèi)部會(huì)做處理,不會(huì)導(dǎo)致端口沖突)

http.createServer((req,res)={

res.writeHead(200);

res.end(helloworld\n

}).listen(8000);

console.log(`啟動(dòng)工作進(jìn)程:${process.pid}`);

}

應(yīng)用實(shí)踐

上面分析了Node.js進(jìn)程退出的各種情況,現(xiàn)在我們來做一個(gè)監(jiān)聽進(jìn)程退出的工具,在Node.js進(jìn)程退出時(shí),允許使用方執(zhí)行自己的退出邏輯:

//exit-hook.js

//保存需要執(zhí)行的退出任務(wù)

consttasks=[];

//添加退出任務(wù)

constaddExitTask=fn=tasks.push(fn);

consthandleExit=(code,error)={

//...handleExit的實(shí)現(xiàn)見下面

//監(jiān)聽各種退出事件

process.on(exit,code=handleExit(code));

//按照POSIX的規(guī)范,我們用128+信號(hào)編號(hào)得到最終的退出碼

//信號(hào)編號(hào)參考下面的圖片,大家可以在linux系統(tǒng)下執(zhí)行kill-l查看所有的信號(hào)編號(hào)

process.on(SIGHUP,()=handleExit(128+1));

process.on(SIGINT,()=handleExit(128+2));

process.on(SIGTERM,()=handleExit(128+15));

//Windows下按下ctrl+break的退出信號(hào)

process.on(SIGBREAK,()=handleExit(128+21));

//退出碼1代表未捕獲的錯(cuò)誤導(dǎo)致進(jìn)程退出

process.on(uncaughtException,error=handleExit(1,error));

process.on(unhandledRejection,error=handleExit(1,error));

信號(hào)編號(hào):

接下來我們要實(shí)現(xiàn)真正的進(jìn)程退出函數(shù)handleExit,因?yàn)橛脩魝魅氲娜蝿?wù)函數(shù)可能是同步的,也可能是異步的;我們可以借助process.nextTick來保證用戶的同步代碼都已經(jīng)執(zhí)行完成,可以簡單理解process.nextTick會(huì)在每個(gè)事件循環(huán)階段的同步代碼執(zhí)行完成后執(zhí)行(理解process.nextTick);針對(duì)異步任務(wù),我們需要用戶調(diào)用callback來告訴我們異步任務(wù)已經(jīng)執(zhí)行完成了:

//標(biāo)記是否正在退出,避免多次執(zhí)行

letisExiting=false;

consthandleExit=(code,error)={

if(isExiting)return;

isExiting=true;

//標(biāo)記已經(jīng)執(zhí)行了退出動(dòng)作,避免多次調(diào)用

lethasDoExit=fasle;

constdoExit=()={

if(hasDoExit)return;

hasDoExit=true

process.nextTick(()=process.exit(code))

//記錄有多少個(gè)異步任務(wù)

letasyncTaskCount=0;

//異步任務(wù)結(jié)束后,用戶需要調(diào)用的回調(diào)

letayncTaskCallback=()={

process.nextTick(()={

asyncTaskCount--

if(asyncTaskCount===0)doExit()

//執(zhí)行所有的退出任務(wù)

tasks.forEach(taskFn={

//如果taskFn函數(shù)的參數(shù)個(gè)數(shù)大于1,認(rèn)為傳遞了callback參數(shù),是一個(gè)異步任務(wù)

if(taskFn.length1){

asyncTaskCount++

taskFn(error,ayncTaskCallback)

}else{

taskFn(error)

//如果存在異步任務(wù)

if(asyncTaskCount0){

//超過10s后,強(qiáng)制退出

setTimeout(()={

doExit();

},10*1000)

}else{

doExit()

};

至此,我們的進(jìn)程退出監(jiān)聽工具就完成了,完整的實(shí)現(xiàn)可以查看這個(gè)開源庫async-exit-hook

/darukjs/daruk-exit-hook

進(jìn)程優(yōu)雅退出

通常我們的webserver在重啟、被運(yùn)行容器調(diào)度(pm2或者docker等)、出現(xiàn)異常導(dǎo)致進(jìn)程退出時(shí),我們希望執(zhí)行退出動(dòng)作,如完成已經(jīng)連接到服務(wù)的請求響應(yīng)、清理數(shù)據(jù)庫連接、打印錯(cuò)誤日志、觸發(fā)告警等,做完退出動(dòng)作后,再退出進(jìn)程,我們可以使用剛才的進(jìn)程退出監(jiān)聽工具實(shí)現(xiàn):

consthttp=require(http

//創(chuàng)建webserver

constserver=http.createServer((req,res)={

res.writeHead(200);

res.end(helloworld\n

}).listen(8000);

//使用我們在上面開發(fā)的工具添加進(jìn)程退出任務(wù)

addExitTask((error,callback)={

//打印錯(cuò)誤日志、觸發(fā)告警、釋放數(shù)據(jù)庫連接等

console.log(進(jìn)程異常退出,error)

//停止接受新的請求

server.close((error)={

if(error){

console.log(停止接受新請求錯(cuò)誤,error)

}else{

console.log(已停止接受新的請求)

//比較簡單的做法是,等待一定的時(shí)間(這里我們等待5s),讓存量請求執(zhí)行完畢

//如果要完全保證所有請求都處理完畢,需要記錄每一個(gè)連接,在所有連接都釋放后,才執(zhí)行退出動(dòng)作

//可以參考開源庫/seb

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論