在 Go 1.16 的更新中,signal
包增長了一個函數 NotifyContext,
這讓咱們優雅的重啓服務(Graceful Restart)能夠寫的更加優雅。git
一個服務想要優雅的重啓主要包含兩個方面:github
Graceful Shutdown
,不強制殺進程,不泄漏系統資源。第二個問題跟部署方式相關,改天專門寫一篇討論,今天咱們主要談怎麼樣優雅的退出。golang
首先在代碼裏,用了外部資源,必定要使用defer
去調用Close()
方法關閉。
而後咱們就要攔截系統的中斷信號,保證程序收到中斷信號以後,主動有序退出,這樣全部的 defer 纔會被執行。docker
在之前,大概是這麼寫:shell
func everLoop(ctx context.Context) { LOOP: for { select { case <-ctx.Done(): // 收到信號退出無限循環 break LOOP default: // 用一個 sleep 模擬業務邏輯 time.Sleep(time.Second * 10) } } } func main() { // 創建一個能夠手動取消的 Context ctx, cancel := context.WithCancel(context.Background()) // 監控系統信號,這裏只監控了 SIGINT(Ctrl+c),SIGTERM // 在 systemd 和 docker 中,都是先發 SIGTERM,過一段時間沒退出再發 SIGKILL // 因此這裏沒捕獲 SIGKILL sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) go func() { <-sig cancel() }() // 開始無限循環,收到信號就會退出 everLoop(ctx) fmt.Println("graceful shuwdown") }
如今有了新的函數,這一段變得更簡單了:微信
func main() { // 監控系統信號和建立 Context 如今一步搞定 ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) // 在收到信號的時候,會自動觸發 ctx 的 Done ,這個 stop 是再也不捕獲註冊的信號的意思,算是一種釋放資源。 defer stop() // 開始無限循環,收到信號就會退出 everLoop(ctx) fmt.Println("graceful shuwdown") }
感謝 Golang
,當年用別的語言須要寫一大堆代碼的功能,如今幾行就能夠輕鬆實現了。
讓它成爲你服務程序的標配吧。函數
最後,我是寫最新的獨立項目LetServerRun的時候,發現這種最新的寫法的。
LetServerRun 可讓你把微信公衆號看成隨身的 Terminal 控制你的服務端。
在它的 Agent 的 main 函數中就有上述用法的示例,
歡迎參考。oop
附上 LetServerRun 的服務號二維碼,感興趣的同窗能夠關注一下:spa