推荐:https://www.jianshu.com/p/ae72ad58ecb6
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
// 优雅退出(退出信号)
func waitElegantExit(signalChan chan os.Signal) {
go func() {
<-signalChan
// 这里做一些清理操作或者输出相关说明,比如 断开数据库连接
fmt.Println("程序退出")
os.Exit(2)
}()
}
func main() {
signalChan := make(chan os.Signal, 1)
// 注册CTRL+C:被打断通道,syscall.SIGINT
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
waitElegantExit(signalChan)
//
// 你的业务逻辑
//
time.Sleep(time.Duration(time.Hour * 1))
}
输出
程序启动
执行任务...
执行任务...
执行任务...
执行任务...
任务被中断,程序退出 # mac/linux 上按Ctrl+C,windows上调试运行,然后点击停止
Process finished with exit code 2
---------------------------- 2020-04-21 更新 ----------------------------
项目中使用的代码和上面大致一样,生产环境中程序崩溃后,没有留下任何异常,调查后发现,因为进程崩溃会触发优雅退出,而在优雅退出的代码里,使用里 os.Exit() ,所以 defer fun()没有执行。正确写法如下:
以下内容有误导,正在更新中。。。
写法一(建议):
func main() {
// ... 业务逻辑
// 优雅退出
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
waitExit(c)
}
func waitExit(c chan os.Signal) {
for i := range c {
switch i {
case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
_ = logger.Sugar.Sync()
logger.Sugar.Info("exit...")
// 如果是崩溃导致的信号,打印错误
err := recover()
if err != nil {
logger.Sugar.Error(err)
}
// 如果 recover()是写在defer 里面的,要使用return
// return
os.Exit(1)
}
}
}
正确写法2:
func main() {
// ... 业务逻辑
defer func(){
// 如果是崩溃导致的信号,打印错误
err := recover()
if err != nil {
logger.Sugar.Error(err)
}
}
// 优雅退出
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
waitExit(c)
}
func waitExit(c chan os.Signal) {
for i := range c {
switch i {
case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
_ = logger.Sugar.Sync()
logger.Sugar.Info("exit...")
// 如果是崩溃导致的信号,打印错误
err := recover()
if err != nil {
logger.Sugar.Error(err)
}
// 如果 recover()是写在defer 里面的,要使用return
return
//os.Exit(1)
}
}
}
名称 | 值 | 动作 | 说明 |
---|---|---|---|
SIGHUP | 1 | Term | 终端控制进程结束(终端连接断开) |
SIGINT | 2 | Term | 用户发送INTR字符(Ctrl+C)触发 |
SIGQUIT | 3 | Core | 用户发送QUIT字符(Ctrl+/)触发 |
SIGILL | 4 | Core | 非法指令(程序错误、试图执行数据段、栈溢出等) |
SIGABRT | 6 | Core | 调用abort函数触发 |
SIGFPE | 8 | Core | 算术运行错误(浮点运算错误、除数为零等) |
SIGKILL | 9 | Term | 无条件结束程序(不能被捕获、阻塞或忽略) |
SIGSEGV | 11 | Core | 无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作) |
SIGPIPE | 13 | Term | 消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作) |
SIGALRM | 14 | Term | 时钟定时信号 |
SIGTERM | 15 | Term | 结束程序(可以被捕获、阻塞或忽略) |
SIGUSR1 | 30,10,16 | Term | 用户保留 |
SIGUSR2 | 31,12,17 | Term | 用户保留 |
SIGCHLD | 20,17,18 | Ign | 子进程结束(由父进程接收) |
SIGCONT | 19,18,25 | Cont | 继续执行已经停止的进程(不能被阻塞) |
SIGSTOP | 17,19,23 | Stop | 停止进程(不能被捕获、阻塞或忽略) |
SIGTSTP | 18,20,24 | Stop | 停止进程(可以被捕获、阻塞或忽略) |
SIGTTIN | 21,21,26 | Stop | 后台程序从终端中读取数据时触发 |
SIGTTOU | 22,22,27 | Stop | 后台程序向终端中写数据时触发 |
博客:https://blog.csdn.net/xmcy001122
我的Golang项目自荐: