聊聊各種可能導致 Node.js 進程退出的情況( 二 )

應用實踐
上面分析了 Node.js 進程退出的各種情況 , 現在我們來做一個監聽進程退出的工具 , 在 Node.js 進程退出時 , 允許使用方執行自己的退出邏輯:
// exit-hook.js// 保存需要執行的退出任務const tasks = [];// 添加退出任務const addExitTask = fn => tasks.push(fn);const handleExit = (code, error) => { // ...handleExit 的實現見下面};// 監聽各種退出事件process.on('exit', code => handleExit(code));// 按照 POSIX 的規范 , 我們用 128 + 信號編號 得到最終的退出碼// 信號編號參考下面的圖片 , 大家可以在 linux 系統下執行 kill -l 查看所有的信號編號process.on('SIGHUP', () => handleExit(128 + 1));process.on('SIGINT', () => handleExit(128 + 2));process.on('SIGTERM', () => handleExit(128 + 15));// windows 下按下 ctrl+break 的退出信號process.on('SIGBREAK', () => handleExit(128 + 21));// 退出碼 1 代表未捕獲的錯誤導致進程退出process.on('uncaughtException', error => handleExit(1, error));process.on('unhandledRejection', error => handleExit(1, error));信號編號:

聊聊各種可能導致 Node.js 進程退出的情況

文章插圖

接下來我們要實現真正的進程退出函數 handleExit , 因為用戶傳入的任務函數可能是同步的 , 也可能是異步的;我們可以借助 process.nextTick 來保證用戶的同步代碼都已經執行完成 , 可以簡單理解 process.nextTick 會在每個事件循環階段的同步代碼執行完成后執行(理解 process.nextTick);針對異步任務 , 我們需要用戶調用 callback 來告訴我們異步任務已經執行完成了:
// 標記是否正在退出 , 避免多次執行let isExiting = false;const handleExit = (code, error) => { if (isExiting) return; isExiting = true; // 標記已經執行了退出動作 , 避免多次調用 let hasDoExit = fasle; const doExit = () => { if (hasDoExit) return; hasDoExit = true process.nextTick(() => process.exit(code)) } // 記錄有多少個異步任務 let asyncTaskCount = 0; // 異步任務結束后 , 用戶需要調用的回調 let ayncTaskCallback = () => { process.nextTick(() => { asyncTaskCount-- if (asyncTaskCount === 0) doExit() }) } // 執行所有的退出任務 tasks.forEach(taskFn => { // 如果 taskFn 函數的參數個數大于 1 , 認為傳遞了 callback 參數 , 是一個異步任務 if (taskFn.length > 1) { asyncTaskCount++ taskFn(error, ayncTaskCallback) } else { taskFn(error) } }); // 如果存在異步任務 if (asyncTaskCount > 0) { // 超過 10s 后 , 強制退出 setTimeout(() => { doExit(); }, 10 * 1000) } else { doExit() }};
至此 , 我們的進程退出監聽工具就完成了 , 完整的實現可以查看這個開源庫 async-exit-hook
https://github.com/darukjs/daruk-exit-hook
進程優雅退出
通常我們的 web server 在重啟、被運行容器調度(pm2 或者 docker 等)、出現異常導致進程退出時 , 我們希望執行退出動作 , 如完成已經連接到服務的請求響應、清理數據庫連接、打印錯誤日志、觸發告警等 , 做完退出動作后 , 再退出進程 , 我們可以使用剛才的進程退出監聽工具實現:
const http = require('http');// 創建 web serverconst server = http.createServer((req, res) => { res.writeHead(200); res.end('hello world\n');}).listen(8000);// 使用我們在上面開發的工具添加進程退出任務addExitTask((error, callback) => { // 打印錯誤日志、觸發告警、釋放數據庫連接等 console.log('進程異常退出' , error) // 停止接受新的請求 server.close((error) => { if (error) { console.log('停止接受新請求錯誤', error) } else { console.log('已停止接受新的請求') } }) // 比較簡單的做法是 , 等待一定的時間(這里我們等待 5s) , 讓存量請求執行完畢 // 如果要完全保證所有請求都處理完畢 , 需要記錄每一個連接 , 在所有連接都釋放后 , 才執行退出動作 // 可以參考開源庫 https://github.com/sebhildebrandt/http-graceful-shutdown setTimout(callback, 5 * 1000)})總結
通過上面的文字 , 相信你已經對導致 Node.js 進程退出的各種情況心里有數了 。 在服務上線后 , 雖然 k8s、pm2 等工具能夠在進程異常退出時 , 不停地拉起進程 , 保證服務的可用性 , 但我們也應該在代碼中主動感知進程的異?;蛘弑徽{度的情況 , 從而能夠更早發現問題 。
更多node相關知識 , 請訪問:nodejs 教程!
以上就是聊聊各種可能導致 Node.js 進程退出的情況的詳細內容 , 更多請關注電腦自學網其它相關文章!
【聊聊各種可能導致 Node.js 進程退出的情況】

推薦閱讀