<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
採用常規方式啟動一個 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 來優雅的終止服務。
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 方法,我們知道在服務終止前,呼叫該方法即可等待活躍連線正常返回,然後優雅的關閉。
但服務啟動後的某一時刻,程式如何知道服務被中斷了呢?服務被中斷時如何通知程式,然後呼叫 Shutdown 作處理呢?接下來看一下系統訊號通知函數的作用。
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
接下來我們使用如上 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 }
到此這篇關於Golang如何優雅的終止一個服務的文章就介紹到這了,更多相關Golang終止服務內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45