




版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 涂料經(jīng)銷合同協(xié)議書
- 工業(yè)保安合同協(xié)議書
- 吉他合同協(xié)議書
- 委托簽合同協(xié)議書
- 后勤合同協(xié)議書
- 備案合同金額協(xié)議書
- 特產(chǎn)買賣合同協(xié)議書
- 公寓樓合伙人合同協(xié)議書
- 購車合同協(xié)議書范文模板
- 承包協(xié)議書合同
- 【MOOC】《學(xué)術(shù)交流英語》(東南大學(xué))章節(jié)中國大學(xué)慕課答案
- 《建筑基坑工程監(jiān)測技術(shù)標(biāo)準(zhǔn)》(50497-2019)
- 《發(fā)育生物學(xué)》課件第八章 胚軸的特化與體軸的建立
- 《中醫(yī)護(hù)理學(xué)》第三章課件
- 泵站畢業(yè)設(shè)計(jì)
- 哈工大環(huán)境工程畢業(yè)設(shè)計(jì)
- 兒科感染性疾病試題
- 行政事業(yè)單位合同業(yè)務(wù)控制流程圖
- 板房區(qū)臨建設(shè)施技術(shù)標(biāo)
- 固定管板式換熱器設(shè)計(jì)說明書
- 美國AAMIST79最新修訂條款解讀----史紹毅[1]
評(píng)論
0/150
提交評(píng)論