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


在 Go 1.16 的更新中,signal包增加了一個函數 NotifyContext
這讓我們優雅的重啟服務(Graceful Restart)可以寫的更加優雅。

一個服務想要優雅的重啟主要包含兩個方面:

  • 退出的舊服務需要 Graceful Shutdown,不強制殺進程,不泄漏系統資源。
  • 在一個集群內輪流重啟服務實例,保證服務不中斷。

第二個問題跟部署方式相關,改天專門寫一篇討論,今天我們主要談怎么樣優雅的退出。

首先在代碼里,用了外部資源,一定要使用defer去調用Close()方法關閉。
然后我們就要攔截系統的中斷信號,保證程序收到中斷信號之后,主動有序退出,這樣所有的 defer 才會被執行。

在以前,大概是這么寫:

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 函數中就有上述用法的示例,
歡迎參考。


Java伴侶


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM