Linux-优雅退出在Golang中的用处和实现

优雅退出在Golang中的实现

  • 代码
  • 陷阱
  • 信号列表
  • 关于作者

推荐: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项目自荐:

  1. IM开源项目。包含Flutter客户端和纯Go实现的服务的,目前支持单聊https://github.com/xmcy0011/CoffeeChat
  2. 链家二手房go爬虫。使用Mongodb做数据分析用,如均价,火车站附近1公里房源等 https://github.com/xmcy0011/getAwayBSG

你可能感兴趣的:(Golang学习)