优服优雅停止主要是通过golang程序监听特定的系统信号,当监听到特定信号时,执行特定业务逻辑,实现相应功能。
一、信号处理
停止进程信号主要是3个,SIGINT,SIGKILL,SIGTERM。
SIGINT
产生方式为键盘ctrl+c,只针对当前前台进程及其所在的进程组的每个进程都发送SIGINT信号,之后这些进程会执行信号处理程序再终止。
SIGTERM
产生方式为使用kill函数,kill + pid 方式发送。当前进程会收到信号,而子进程不会收到,如果当前进程被kill,则其子进程的父进程将为init,即pid为1的进程。
SIGKILL
产生方式为使用kill函数,kill -9 + pid方式发送,当前进程收到该信号,会直接发送默认行为(该信号无法捕获,即无法处理)。
注意
SIGINT、SIGTERM可以捕获,而SIGKILL无法捕获。所以我们主要是监听这两个信号,执行相应的业务逻辑,实现对应功能。
二、用到的package
1、net/http
golang原生http包自带shutdown函数,执行该函数时会关闭所有打开的监听器,不再建立新的连接,同时等待现有连接将对应的业务逻辑执行完毕后,将其关闭。
注意:
shutdown函数无法关闭长链接,如websocket。
serve := http.Server{
Addr: url,
Handler: s.Router,
}
//coding
if err := serve.Shutdown(ctx); err != nil {
log.Fatal("http shutdown failed",err)
}
2、os、os/signal、syscall
使用os/signal监听指定信号,由于signal不会阻塞,需要使用缓冲区为1的channel配合实现程序阻塞。
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
//coding
三、优雅关闭核心代码
serve := http.Server{
Addr: url,
Handler: s.Router,
}
go func() {
if err := serve.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatal("server listen err", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5)
defer cancel()
if err := serve.Shutdown(ctx); err != nil {
log.Fatal("server Shutdown err", err)
}