web服务优雅退出方案

记录在 gin 框架中优雅退出的实现方式。

系统退出信号

信号(Signal)是 linux 中进程间通信的一种方式,当信号发送到某个进程中时,操作系统会中断该进程的正常流程,并进入相应的信号处理函数执行操作,完成后再回到中断的地方继续执行。
比如在中端中键入某些组合键会导致系统发送某些信号给该进程。
常见的信号有:

  • SIGINT:中断信号,通常是键盘上的 Ctrl+C
  • SIGTERM:终止信号,通常是 kill 命令
  • SIGQUIT:退出信号,通常是键盘上的 Ctrl+\
  • SIGKILL:强制终止信号,通常是 kill -9 命令
  • SIGSTOP:停止信号,通常是 kill -STOP 命令

其中 SIGKILLSIGSTOP 信号是无法被捕获的,即使注册了信号处理函数也不会执行。

go 中的信号接收与处理

go 中可以创建一个接收 os.Signal 类型的 channel 变量,然后调用 signal.Notify 函数注册想要拦截的系统信号,将接收到的信号转发到该 channel 变量中,然后在 channel 变量中读取信号即可。

gin 中的优雅退出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var (
	router = gin.Default()
	server *http.Server
)

func Run() {
	getRoutes()
	server = &http.Server{
		Addr:           ":8080",
		Handler:        router,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	go func() {
		// 服务连接
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatal("listen: %s\n", log.Err(err))
		}
	}()
	gracefulExit(server)
	// router.Run(":8080")
}

func getRoutes() {
	douyin := router.Group("/douyin")
	addUserRoutes(douyin)

}

func gracefulExit(server *http.Server) {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)
	sig := <-ch
	log.Info("receive exit signal", log.Any("", sig))
	now := time.Now()
	cxt, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	err := server.Shutdown(cxt)
	if err != nil {
		log.Error("server shutdown error", log.Err(err))
	}

	// 实际退出所耗费的时间
	log.Info("------exited--------", log.Duration("duration:", time.Since(now)))
}
0%