[Go] 捕捉信号以优雅地关闭服务器进程

陪她去流浪 桃子 2019年07月12日 编辑 阅读次数:3099

在开发服务器相关的程序时,时常需要**优雅地(gracefully)**关闭服务器进程。这就需要捕捉并处理操作系统对进程产生的信号。

  • 当服务器进程在前台运行时,我们通常用^C结束它,这时,会产生SIGINT信号
  • 当服务器进程在后台运行时,我们通常用kill $pid结束它,这里,会产生SIGTERM信号

以下代码示例在 Golang 中处理信号,并优雅地关闭服务器然后退出:

 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
package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	server := http.Server{}

	// 把服务器放到 goroutine 中去执行
	go func() {
		if err := server.ListenAndServe(); err != nil {
			// 手动调用 Shutdown 时会产生这个错误
			if err != http.ErrServerClosed {
				log.Println(err)
			}
		}
	}()

	log.Printf("server pid: %d\n", os.Getpid())

	// 捕捉指定的信号
	quit := make(chan os.Signal)
	// 前台时,按 ^C 时触发
	signal.Notify(quit, syscall.SIGINT)
	// 后台时,kill 时触发。kill -9 时的信号 SIGKILL 不能捕捉,所以不用添加
	signal.Notify(quit, syscall.SIGTERM)

	// 等待退出信号
	sig := <-quit
	log.Printf("received signal: %v\n", sig)

	// 收到信号后,优雅地关闭服务器
	log.Println("server shutting down")
	if err := server.Shutdown(context.Background()); err != nil {
		log.Println(err)
	}
	log.Println("server shutted down")
}

参考资料

标签:HTTP · Go · 信号