使用 Go 1.16 的 signal.NotifyContext 讓你的服務重啓更優雅

在 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

LetServerRun

相關文章
相關標籤/搜索