在日常開發過程中,經常會用到 pm2 來起到服務,甚至會用 watch 來實現熱更新。
但上面這種模式並不適合用在生產環境中,因為 pm2 零延時重啓,會導致正在處理的請求直接被銷燬,如果數據庫沒加事務,直接就會導致數據庫的數據不完整。
處理方法是延時 kill,參數是--kill-timeout。
在啓動的app.js中添加SIGINT的監聽,並在回調中用剩餘的timeout時間處理數據。
process.on('SIGINT', function() {
db.stop(function(err) {
process.exit(err ? 1 : 0)
})
})
啓動方式如下:
pm2 start app.js --kill-timeout 3000
重載就會觸發。
pm2 reload
實際問題
一般使用 pm2,我們是希望 pm2 給我們提供負載均衡,每次 pull 好代碼,reload 一下就能升級到最新的服務,而不需要從 Nginx 上下手。但是默認情況下 pm2 直接重啓會導致正在處理的請求丟失。所以我們希望 PM2 在重啓前,可以先通知 App,並給足夠的時間讓 App 處理完正在處理的請求,並且不再接收新的請求,然後再重啓服務。
我們可以把上面的例子改成這樣。
process.on('SIGINT', function() {
global.app.running = false;
db.stop(function(err) {
process.exit(err ? 1 : 0)
})
})
global.app.running為整個服務的全局變量,只要服務啓動就賦值為true,並且在所有理由攔截器中增加判斷邏輯:只要true時才接收請求,這樣就能保證當global.app.running為false時,正在處理的請求不會被銷燬,所有後來進入的請求都失敗,從而保證數據庫完整性。
但是這種攔截方式太過於粗暴,只是保證數據庫完整,但是依然達不到平滑,平滑升級還有很多路要走。
參考鏈接
Graceful Stop