go资深之路笔记(五)用系统信号实现优雅关机
作用:程序结束的时候可能需要一定的时间去处理,比如mysql连接,rediss处理等,这个时候我们可以通过特定系统信号来通知go程序,go程序这边会监听覆盖这些系统信号,使得go程序可以自己控制关闭时间。
核心代码
quit := make(chan os.Signal, 1) // 创建一个系统信号chan
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 注册信号
<-quit // 阻塞,直到收到信号
实现代码 main.go:
package mainimport ("context""log""net/http""os""os/signal""syscall""time"
)func main() {// 创建一个自定义的 ServerMux 和 Servermux := http.NewServeMux()mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {time.Sleep(5 * time.Second) // 模拟一个长请求w.Write([]byte("All done!\n"))})srv := &http.Server{Addr: ":8081",Handler: mux,}// 在一个单独的 goroutine 中启动服务器go func() {if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("ListenAndServe error: %v", err)}log.Println("Server stopped")}()// 创建一个通道来接收操作系统信号quit := make(chan os.Signal, 1)// 监听 SIGINT (Ctrl+C) 和 SIGTERM (docker stop, k8s termination)signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quit // 阻塞,直到收到信号log.Println("Shutting down server...")// 创建一个具有超时的上下文,用于优雅关机// 给正在处理的请求 10 秒时间来完成ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)defer cancel()// 开始优雅关机:停止接受新请求,阻塞直到所有现有请求处理完毕或超时if err := srv.Shutdown(ctx); err != nil {log.Fatalf("Server forced to shutdown: %v", err)}// 此处可以安全地关闭数据库、缓存等连接// db.Close()// redisConn.Close()log.Println("Server exited gracefully")
}
执行:
go run main.go // go test 会直接关闭,似乎没有正确覆盖。
执行成功后,访问:
127.0.0.1:8081
然后用ctrl+C快速关闭执行的程序,就会得到打印:
ps:如果不监听信号syscall.SIGINT直接ctrl+C,会得到:
常见系统信号:
不可捕抓信号:
SIGKILL(9) 强制关闭 对应Linux命令: kill -9 pid
SIGSTOP(19)强制暂停 对应Linux命令: kill -19 pid
SIGCONT(18) 恢复会暂停的进程(包括 SIGSTOP和SIGSTP)
可捕信号:
SIGINT 关闭程序 对应linux命令 ctrl+C
SIGTERM 关闭终端 对应linux命令 kill pid
SIGUSR1 自定义信号 对应linux 命令 kill -10 pid
SIGQUIT 终止并生成 core dump 对应linux命令 kill -3 pid 或者 ctrl +
SIGSTP 终端暂停 对应Linux命令: kill -20 pid 或者 ctrl +z(这个会放入后台)