大家好,我是 Yubin
在 Cloud Native 的世界,應用程式多數以 Container 的形式存在,輕量且啟動快速,但也可能常常會壞掉然後被重啟。
我們要如何在程式接收到結束的訊號的時候,可以優雅的關閉?
什麼是 Graceful Shutdown?
想像一個情境,你在打電動打到一半,突然你媽叫你關機不要再打了。
你會直接把電源切掉?還是先存檔再關掉?
如果你會先存檔再關機,那你就執行了 Graceful Shutdown。
Graceful Shutdown 並不是 Container 才需要注意的,任何程式的設計上都要考慮到如何正確的執行收尾的動作。
如果想要一個程式關閉,可以觸發程式設定好的結束方法,讓應用程式自己執行結束的動作。
也可以透過 OS 或其他方式指令工具對那個程式送出結束的訊號給應用程式。
注意,這邊是送出結束的訊號,並不是不管三七二十一就把那個程式的執行序刪掉。
在 Container 的世界,每個 Container 都很容易被關閉被重啟。
如果被關閉的時候沒有正確的釋放資源,那累積起來勢必會造成嚴重問題 (Connection 用完或 Memory Leak 等)。
那麼要如何實作?
實作的方式很簡單,只要去聽那些代表結束的訊號,聽到的時候不要馬上關閉,先停止接收新的 Request,並執行一系列的釋放資源或寫 log 或其他相應的動作就可以了。
更簡單的實作方式:透過 @godaddy/terminus。
terminus 是 Godaddy 維護的 Graceful Shutdown 函式庫,可以幫我們偵測結束訊號,我們只需要定義收到事件後要做的工作。
透過 npm 進行安裝:
npm i @godaddy/terminus
在程式中,使用 createTerminus
來啟動,並帶入要做的動作設定。
import fastify, { FastifyInstance } from 'fastify'
import { createTerminus } from '@godaddy/terminus'
function onSignal() {
console.log('server is starting cleanup')
return Promise.all([
// your clean logic, like closing database connections
])
}
async function onShutdown() {
console.log('cleanup finished, server is shutting down')
}
const server: FastifyInstance = fastify()
createTerminus(server.server, {
onShutdown,
onSignal
})
createTerminus()
第一個參數是 NodeJS 的 Http Server,我們用的是 FastifyInstance,所以透過 .server
拿到 FastifyInstance 中的 RawServerDefault
。
接收到結束訊號後,會執行 onSignal()
,可以定義清除資源的動作在這邊。
onSignal() 執行完會接著執行 onShutdown()
,才會把 server 進行關閉。
上述程式執行起來後,可以利用 kill
指令把程式的 process 關閉,來觀察 App 收到結束訊號的反應。
會輸出以下文字:
server is starting cleanup
cleanup finished, server is shutting down
Terminated
注意使用
kill
指令不要加上-9
的參數。
更多 terminus 的功能及使用可以參考官方 GitHub。
本篇文章介紹了什麼是 Graceful Shutdown,以及用 terminus 這個非常熱門的套件來實作。
在 Fastify 生態系中也有人開發了 fastify-graceful-shutdown 這樣的 Plugin,只是 terminus 的功能相對完善,且彈性更高。
以上完整範例可以參考 GitHub: Fastify101。
最後一篇剛好寫個 Graceful Shutdown,我也要 Shutdown 了。
感謝大家收看。