IT教程 ·

golang中使用Shutdown特征对http服务举行优雅退出使用总结

Scala函数式编程(五) 函数式的错误处理

golang 程序启动一个 http 效劳时,若效劳被不测住手或中断,会让现有要求衔接倏忽中断,未处置惩罚完成的使命也会涌现不可预知的毛病,如许即会形成效劳硬住手;为了处置惩罚硬住手问题我们愿望效劳中断或退出时将正在处置惩罚的要求一般返回而且守候效劳住手前作的一些必要的处置惩罚工作。

我们能够看一个硬住手的例子:

mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
   time.Sleep(5 * time.Second)
   fmt.Fprintln(w, "Hello world!")
})
server := &http.Server{
   Addr:         ":8080",
   Handler:      mux,
}
server.ListenAndServe()

启动效劳后,我们能够接见 http://127.0.0.1:8080 页面守候 5s 会输出一个 “Hello world!”, 我们能够尝试 Ctrl+C 住手程序,能够看到浏览器马上就显现没法衔接,这示意衔接马上就中断了,退出前的要求也未一般返回。

在 Golang1.8 今后 http 效劳有个新特征 Shutdown 要领能够文雅的封闭一个 http 效劳, 该要领须要传入一个 Context 参数,当程序住手时个中不会中断活泼的衔接,会守候活泼衔接闲置或 Context 住手(手动 cancle 或超时)末了才住手程序,官方文档详见:https://godoc.org/net/http#Server.Shutdown

 

在具体用运用中我们能够合营 signal.Notify 函数来监听系统退出信号来完成程序文雅退出;

特别注重:server.ListenAndServe() 要领在 Shutdown 时会马上返回,Shutdown 要领会壅塞至一切衔接闲置或 context 完成,所以 Shutdown 的要领要写在主 goroutine 中

 

文雅退出试验1:

func main() {
   mux := http.NewServeMux()
   mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      time.Sleep(5 * time.Second)
      fmt.Fprintln(w, "Hello world!")
   })
   server := &http.Server{
      Addr:         ":8080",
      Handler:      mux,
   }
   go server.ListenAndServe()

   listenSignal(context.Background(), server)
}

func listenSignal(ctx context.Context, httpSrv *http.Server) {
   sigs := make(chan os.Signal, 1)
   signal.Notify(sigs, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

   select {
   case <-sigs:
      fmt.Println("notify sigs")
      httpSrv.Shutdown(ctx)
      fmt.Println("http shutdown")
   }
}

我们建立了一个 listenSignal 函数来监听程序退出信号 listenSignal 函数中的 select 会一向壅塞直到收到退出信号,然后实行 Shutdown(ctx) 。

能够看到,我们是从新开启了一个 goroutine 来启动 http 效劳监听,而 Shutdown(ctx) 在主 goroutine 中,如许才守候一切衔接闲置后再退出程序。

启动上述程序,我们接见  http://127.0.0.1:8080 页面守候 5s 会输出一个 “Hello world!” 在守候时期,我们能够尝试 Ctrl+C 封闭程序,能够看程序控制台会守候输出后才打印 http shutdown 同时浏览器会显现输出内容;而封闭程序以后再新开一个浏览器窗口接见 http://127.0.0.1:8080 则新开的窗口直接断开没法接见。(这些操纵须要在 5s 内完成,能够恰当调解处置惩罚时候轻易我们视察试验效果)

经由过程该试验我们能看到,Shutdown(ctx) 会阻挠新的衔接进入并守候活泼衔接处置惩罚完成后再住手程序,到达文雅退出的目标。

固然我们还能够进一步证实 Shutdown(ctx) 除了守候活泼衔接的同时也会监听 Context 完成事宜,两者有一个触发都邑触发程序住手;

我们将代码稍作修正以下:

func main() {
   mux := http.NewServeMux()
   mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      time.Sleep(10 * time.Second)
      fmt.Fprintln(w, "Hello world!")
   })
   server := &http.Server{
      Addr:         ":8080",
      Handler:      mux,
   }
   go server.ListenAndServe()

   listenSignal(context.Background(), server)
}

func listenSignal(ctx context.Context, httpSrv *http.Server) {
   sigs := make(chan os.Signal, 1)
   signal.Notify(sigs, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

   select {
   case <-sigs:
      timeoutCtx,_ := context.WithTimeout(ctx, 3*time.Second)
      fmt.Println("notify sigs")
      httpSrv.Shutdown(timeoutCtx)
      fmt.Println("http shutdown")
   }
}

我们将 http 效劳处置惩罚修正成守候 10s, 监听到退出事宜后 ctx 修正成 3s 超时的 Context,运转上述程序,然后 Ctrl+C 发送完毕信号,我们能够直观的看到,程序在守候 3s 后就住手了,此时纵然 http 效劳中的处置惩罚还没完成,程序也住手了,浏览器中也直接中断衔接了。

须要注重的问题:我们在 HandleFunc 中编写的处置惩罚逻辑都是在主 goroutine 中完成的和 Shotdown 要领是一个同步操纵,因而 Shutdown(ctx) 会守候完成,假如我们的处置惩罚逻辑是在新的 goroutine 中或是一个像 Websock 如许的长衔接,则Shutdown(ctx) 不会守候处置惩罚完成,假如须要处置惩罚这类问题照样须要应用 sync.WaitGroup 来举行同步守候。

 

手艺总结:

1. Shutdown 要领要写在主 goroutine 中;

2.在主 goroutine 中的处置惩罚逻辑才会壅塞守候处置惩罚;

3.带超时的 Context 是在建立时就入手下手计时了,因而须要在接收到完毕信号后再建立带超时的 Context。

 

qt creator源码全方面分析(2-10-2)

参与评论