现在,一个用 Go 编写的 Web 服务器正在 docker 容器中运行。 当停止 docker 容器执行
docker stop
命令时,我想优雅地关闭 Web 服务器。
这种情况下,是否可以像下面这样执行优雅关机呢?
当前代码
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) // I don't know what signal to catch
defer stop()
srv := &http.Server{
Addr: port,
Handler: mux,
}
go srv.ListenAndServe()
<-ctx.Done()
// do someshing
log.Println("server will shutdown gracefully.")
err = srv.Shutdown(ctx)
if err != nil {
return err
}
SIGTERM
是要收听的正确信号。然而,您的代码可以进行一些改进。
ListenAndServe()
的返回值(可能是 context.Canceled
,但不一定是)。Shutdown()
提供已取消的上下文。如果这需要太多时间,我们将受到SIGKILL
(我们实际上不会收到的信号)的影响,所以我们可以只提供context.Background()
。这为我们提供了以下信息:
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM)
defer stop()
srv := &http.Server{
Addr: port,
Handler: mux,
}
go func() {
err := srv.ListenAndServe()
log.Printf("ListenAndServe() returned: %v", err)
}()
<-ctx.Done()
log.Println("server will shutdown gracefully.")
return srv.Shutdown(context.Background())
重要的一点是,
Shutdown()
将耐心等待活动连接,该连接可能会也可能不会及时完成。也可以在处理程序中使用 signal.NotifyContext()
,尝试快速完成剩余工作,以防收到 SIGTERM
。我建议改为向 --time
提供适当的 docker container stop
参数(以防默认 10 秒不够)。