日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

Golang 優雅的終止一個服務

 

采用常規方式啟動一個Golang http服務時,若服務被意外終止或中斷,即未等待服務對現有請求連接處理并正常返回且亦未對服務停止前作一些必要的處理工作,這樣即會造成服務硬終止。這種方式不是很優雅。

參看如下代碼,該http服務請求路徑為根路徑,請求該路徑,其會在2s后返回hello。

var addr = flag.String("server addr", ":8080", "server address")
func main() {
 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 time.Sleep(2 * time.Second)
 fmt.Fprintln(w, "hello")
 })
 http.ListenAndServe(*addr, nil)
}

若服務啟動后,請求http://localhost:8080/,然后使用Ctrl+C立即中斷服務,服務即會立即退出(exit status 2),請求未正常返回(ERR_CONNECTION_REFUSED),連接即馬上斷了。

接下來介紹使用http.Server的Shutdown方法結合signal.Notify來優雅的終止服務。

1 Shutdown方法

Golang http.Server結構體有一個終止服務的方法Shutdown,其go doc如下。

func (srv *Server) Shutdown(ctx context.Context) error
 Shutdown gracefully shuts down the server without interrupting any active
 connections. Shutdown works by first closing all open listeners, then
 closing all idle connections, and then waiting indefinitely for connections
 to return to idle and then shut down. If the provided context expires before
 the shutdown is complete, Shutdown returns the context's error, otherwise it
 returns any error returned from closing the Server's underlying Listener(s).
 When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS
 immediately return ErrServerClosed. Make sure the program doesn't exit and
 waits instead for Shutdown to return.
 Shutdown does not attempt to close nor wait for hijacked connections such as
 WebSockets. The caller of Shutdown should separately notify such long-lived
 connections of shutdown and wait for them to close, if desired. See
 RegisterOnShutdown for a way to register shutdown notification functions.
 Once Shutdown has been called on a server, it may not be reused; future
 calls to methods such as Serve will return ErrServerClosed.

由文檔可知:

使用Shutdown可以優雅的終止服務,其不會中斷活躍連接。

其工作過程為:首先關閉所有開啟的監聽器,然后關閉所有閑置連接,最后等待活躍的連接均閑置了才終止服務。

若傳入的context在服務完成終止前已超時,則Shutdown方法返回context的錯誤,否則返回任何由關閉服務監聽器所引起的錯誤。

當Shutdown方法被調用時,Serve、ListenAndServe及ListenAndServeTLS方法會立刻返回ErrServerClosed錯誤。請確保Shutdown未返回時,勿退出程序。

對諸如WebSocket等的長連接,Shutdown不會嘗試關閉也不會等待這些連接。若需要,需調用者分開額外處理(諸如通知諸長連接或等待它們關閉,使用RegisterOnShutdown注冊終止通知函數)。

一旦對server調用了Shutdown,其即不可再使用了(會報ErrServerClosed錯誤)。

有了Shutdown方法,我們知道在服務終止前,調用該方法即可等待活躍連接正常返回,然后優雅的關閉。

關于上面用到的Golang Context參數,之前專門寫過一篇文章介紹了Context的使用場景(請參考:Context 你使用過了吧?本文和你一起總結 Golang Context 的使用)。

但服務啟動后的某一時刻,程序如何知道服務被中斷了呢?服務被中斷時如何通知程序,然后調用Shutdown作處理呢?接下來看一下系統信號通知函數的作用。

2 signal.Notify函數

signal包的Notify函數提供系統信號通知的能力,其go doc如下。

func Notify(c chan<- os.Signal, sig ...os.Signal)
 Notify causes package signal to relay incoming signals to c. If no signals
 are provided, all incoming signals will be relayed to c. Otherwise, just the
 provided signals will.
 Package signal will not block sending to c: the caller must ensure that c
 has sufficient buffer space to keep up with the expected signal rate. For a
 channel used for notification of just one signal value, a buffer of size 1
 is sufficient.
 It is allowed to call Notify multiple times with the same channel: each call
 expands the set of signals sent to that channel. The only way to remove
 signals from the set is to call Stop.
 It is allowed to call Notify multiple times with different channels and the
 same signals: each channel receives copies of incoming signals
 independently.

由文檔可知:

參數c是調用者的信號接收通道,Notify可將進入的信號轉到c。sig參數為需要轉發的信號類型,若不指定,所有進入的信號都將會轉到c。

信號不會阻塞式的發給c:調用者需確保c有足夠的緩沖空間,以應對指定信號的高頻發送。對于用于通知僅一個信號值的通道,緩沖大小為1即可。

同一個通道可以調用Notify多次:每個調用擴展了發送至該通道的信號集合。僅可調用Stop來從信號集合移除信號。

允許不同的通道使用同樣的信號參數調用Notify多次:每個通道獨立的接收進入信號的副本。

綜上,有了signal.Notify,傳入一個chan并指定中斷參數,這樣當系統中斷時,即可接收到信號。

參看如下代碼,當使用Ctrl+C時,c會接收到中斷信號,程序會在打印“program interrupted”語句后退出。

func main() {
 c := make(chan os.Signal)
 signal.Notify(c, os.Interrupt)
 <-c
 log.Fatal("program interrupted")
}
$ go run main.go
Ctrl+C
2019/06/11 17:59:11 program interrupted
exit status 1

3 Server優雅的終止

接下來我們使用如上signal.Notify結合http.Server的Shutdown方法實現服務優雅的終止。

如下代碼,Handler與文章開始時的處理邏輯一樣,其會在2s后返回hello。

創建一個http.Server實例,指定端口與Handler。

聲明一個processed chan,其用來保證服務優雅的終止后再退出主goroutine。

新啟一個goroutine,其會監聽os.Interrupt信號,一旦服務被中斷即調用服務的Shutdown方法,確保活躍連接的正常返回(本代碼使用的Context超時時間為3s,大于服務Handler的處理時間,所以不會超時)。

處理完成后,關閉processed通道,最后主goroutine退出。

代碼同時托管在GitHub,歡迎關注(github.com/olzhy/go-excercises)。

var addr = flag.String("server addr", ":8080", "server address")
func main() {
 // handler
 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 time.Sleep(2 * time.Second)
 fmt.Fprintln(w, "hello")
 })
 // server
 srv := http.Server{
 Addr: *addr,
 Handler: handler,
 }
 // make sure idle connections returned
 processed := make(chan struct{})
 go func() {
 c := make(chan os.Signal, 1)
 signal.Notify(c, os.Interrupt)
 <-c
 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
 defer cancel()
 if err := srv.Shutdown(ctx); nil != err {
 log.Fatalf("server shutdown failed, err: %vn", err)
 }
 log.Println("server gracefully shutdown")
 close(processed)
 }()
 // serve
 err := srv.ListenAndServe()
 if http.ErrServerClosed != err {
 log.Fatalf("server not gracefully shutdown, err :%vn", err)
 }
 // waiting for goroutine above processed
 <-processed
}

原文鏈接:https://leileiluoluo.com/posts/golang-shutdown-server-gracefully.html

本文作者:磊磊落落的博客,原創授權發布

分享到:
標簽:Golang
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定