go如何優(yōu)雅關(guān)閉Graceful?Shutdown服務(wù)
Shutdown方法
Go1.8之后有了Shutdown方法,用來優(yōu)雅的關(guān)閉(Graceful Shutdown)服務(wù)。
func (srv *Server) Shutdown(ctx context.Context) error
這個方法會在調(diào)用時進(jìn)行下述操作:
1.關(guān)閉所有open listeners
2.關(guān)閉所有idle connections
3.無限期等待connections回歸idle狀態(tài)
4.之后關(guān)閉服務(wù)
注:3的無限期等待可以用context的超時來解決。
RegisterOnShutdown方法
Go1.9之后有了RegisterOnShutdown方法,用來在優(yōu)雅的關(guān)閉服務(wù)的同時處理一些退出任務(wù)。
func (srv *Server) RegisterOnShutdown(f func())
關(guān)于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進(jìn)行注冊之后,這些函數(shù)被放入一個函數(shù)切片中,并在Shutdown被調(diào)用的時候,為每個函數(shù)啟動了一個goroutine進(jìn)行處理。
但由于沒有進(jìn)行后續(xù)處理,于是這里有個小問題,Shutdown是不等待這些處理函數(shù)結(jié)束的,就可能比這些處理函數(shù)先完成并進(jìn)而結(jié)束程序。
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
}運(yùn)行時Ctrl+c之后,服務(wù)立刻結(jié)束了,并未等待log.Println("Exit func")的輸出。
sync.WaitGroup處理退出函數(shù)
為了解決這個問題我們使用sync.WaitGroup來處理這些退出函數(shù),于是我們有了下面的代碼(解耦并增加了一些日志):
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"之后進(jìn)行等待,等待退出程序完成任務(wù)。
執(zhí)行日志如下:
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
可以看到這個服務(wù)本身很輕,Shutdown start之后立刻就finish并來到wg.Wait(),并等待register的函數(shù)完成之后再退出程序。
另外,如果在服務(wù)運(yùn)行的過程中需要啟動一些其他的任務(wù),也可以使用
wg.Add(1)
go func() {
defer wg.Done()
...
}來保證任務(wù)在服務(wù)Shutdown之后可以順利完成。
以上就是go如何優(yōu)雅關(guān)閉Graceful Shutdown服務(wù)的詳細(xì)內(nèi)容,更多關(guān)于go關(guān)閉Graceful Shutdown的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用介紹
這篇文章主要介紹了Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-05-05
Golang常見錯誤之值拷貝和for循環(huán)中的單一變量詳解
這篇文章主要給大家介紹了關(guān)于Golang常見錯誤之值拷貝和for循環(huán)中單一變量的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
破解IDEA(Goland)注冊碼設(shè)置 license server一直有效不過期的過程詳解
這篇文章主要介紹了破解IDEA(Goland)注冊碼設(shè)置 license server一直有效不過期,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
go語言執(zhí)行等待直到后臺goroutine執(zhí)行完成實(shí)例分析
這篇文章主要介紹了go語言執(zhí)行等待直到后臺goroutine執(zhí)行完成的方法,實(shí)例分析了Go語言中WaitGroup的使用技巧,需要的朋友可以參考下2015-03-03

