您好,登錄后才能下訂單哦!
本篇文章和大家了解一下go關閉Graceful Shutdown服務的幾種方法。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。
Shutdown方法
RegisterOnShutdown方法
sync.WaitGroup處理退出函數
Go1.8之后有了Shutdown方法,用來優雅的關閉(Graceful Shutdown)服務。
func (srv *Server) Shutdown(ctx context.Context) error
這個方法會在調用時進行下述操作:
1.關閉所有open listeners
2.關閉所有idle connections
3.無限期等待connections回歸idle狀態
4.之后關閉服務
注:3的無限期等待可以用context的超時來解決。
Go1.9之后有了RegisterOnShutdown方法,用來在優雅的關閉服務的同時處理一些退出任務。
func (srv *Server) RegisterOnShutdown(f func())
關于RegisterOnShutdown如何生效,我們看代碼。
type Server struct { ... onShutdown []func() } func (srv *Server) RegisterOnShutdown(f func()) { srv.mu.Lock() srv.onShutdown = append(srv.onShutdown, f) srv.mu.Unlock() } func (srv *Server) Shutdown(ctx context.Context) error { ... srv.mu.Lock() ... for _, f := range srv.onShutdown { go f() } srv.mu.Unlock() ... }
由代碼可知,在通過RegisterOnShutdown進行注冊之后,這些函數被放入一個函數切片中,并在Shutdown被調用的時候,為每個函數啟動了一個goroutine進行處理。
但由于沒有進行后續處理,于是這里有個小問題,Shutdown是不等待這些處理函數結束的,就可能比這些處理函數先完成并進而結束程序。
package main import ( "context" "log" "net/http" "os" "os/signal" "time" ) func main() { var srv http.Server srv.RegisterOnShutdown(func() { time.Sleep(3 * time.Second) log.Println("Exit func") }) idleConnsClosed := make(chan struct{}) go func() { sigint := make(chan os.Signal, 1) signal.Notify(sigint, os.Interrupt) <-sigint // We received an interrupt signal, shut down. if err := srv.Shutdown(context.Background()); err != nil { // Error from closing listeners, or context timeout: log.Printf("HTTP server Shutdown: %v", err) } close(idleConnsClosed) }() if err := srv.ListenAndServe(); err != http.ErrServerClosed { // Error starting or closing listener: log.Fatalf("HTTP server ListenAndServe: %v", err) } <-idleConnsClosed }
運行時Ctrl+c之后,服務立刻結束了,并未等待log.Println("Exit func")的輸出。
為了解決這個問題我們使用sync.WaitGroup來處理這些退出函數,于是我們有了下面的代碼(解耦并增加了一些日志):
package main import ( "context" "log" "net/http" "os" "os/signal" "sync" "time" ) func main() { var srv http.Server var wg sync.WaitGroup register := func(f func()) { wg.Add(1) log.Println("Exit func register") srv.RegisterOnShutdown(func() { defer wg.Done() f() log.Println("Exit func done") }) } register(func() { time.Sleep(3 * time.Second) log.Println("Called on Shutdown") }) idleConnsClosed := make(chan struct{}) go func() { sigint := make(chan os.Signal, 1) signal.Notify(sigint, os.Interrupt) <-sigint // We received an interrupt signal, shut down. log.Println("Shutdown start") if err := srv.Shutdown(context.Background()); err != nil { // Error from closing listeners, or context timeout: log.Printf("HTTP server Shutdown: %v", err) } close(idleConnsClosed) }() if err := srv.ListenAndServe(); err != http.ErrServerClosed { // Error starting or closing listener: log.Fatalf("HTTP server ListenAndServe: %v", err) } <-idleConnsClosed log.Println("Shutdown finish") wg.Wait() }
我們在RegisterOnShutdown時使用了time.Sleep(3 * time.Second)來模擬一個長時間的退出程序,并且在"Shutdown finish"之后進行等待,等待退出程序完成任務。
執行日志如下:
2023/05/12 16:48:53 Exit func register
^C2023/05/12 16:48:54 Shutdown start
2023/05/12 16:48:54 Shutdown finish
2023/05/12 16:48:57 Called on Shutdown
2023/05/12 16:48:57 Exit func done
可以看到這個服務本身很輕,Shutdown start之后立刻就finish并來到wg.Wait(),并等待register的函數完成之后再退出程序。
另外,如果在服務運行的過程中需要啟動一些其他的任務,也可以使用
wg.Add(1) go func() { defer wg.Done() ... }
來保證任務在服務Shutdown之后可以順利完成。
以上就是go關閉Graceful Shutdown服務的幾種方法的簡略介紹,當然詳細使用上面的不同還得要大家自己使用過才領會。如果想了解更多,歡迎關注億速云行業資訊頻道哦!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。