golang代码笔记 -- 优雅退出的程序(服务)

主要内容

1. 程序为什么需要优雅退出
2. 实现办法(举例)
3. 总结

1. 程序为什么需要优雅退出

原因很简单,我们都不希望自己的程序被异常关闭或者ctrl+c给直接干掉,或许我们这回正在写数据库,或许正在完成一个复杂的计算流程;我们希望程序能在完成手头的工作之后才关闭,就好比编辑器退出是自动保存一样,防止之前的工作白费,更糟糕的是,导致异常或者不一致的数据;

2.实现办法(举例)

其实实现办法很简单,golang提供了现成的包来帮助我们解决这个问题:

signal.Notify(c chan<- os.Signal, sig ...os.Signal)

第一个参数:一个接受信号的通道
其他参数:为需要捕获的系统信号的数组
当相关系统信号被触发时,c中将会有数据,可通过c来阻塞程序,来实现在接到系统信号后自定义处理逻辑;

关于所有系统信号的介绍可参考:https://godoc.org/os/signal

其次,就是context的使用,用于在不同协程之间完成通信;要实现优雅退出,需要通过context将程序结束的消息通知到各个正在处理的协程,让他们做好退出准备(只处理手头的任务);

ctx, cancel := context.WithCancel(context.Background())

context.WithCancel返回一个新的context和与之对应的cancel函数,调用cancel函数,将会降Done的信号通知到所有正在使用ctx的协程;

完整示例

package main

import (
	"syscall"
	"os"
	"os/signal"
	"time"
	"context"
	"log"
)

func main() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGHUP)
	ctx, cancel := context.WithCancel(context.Background())
	line := make(chan int, 1)
	exit := make(chan bool, 1)
	go Producer(line)
	go WorkFunc(ctx, line, exit)
	<- sigs
	cancel()
	<- exit
}

// 工作协程
func WorkFunc(ctx context.Context, line chan int, exit chan bool) {
	for {
		select {
		case n := <- line:
			log.Println("work start:", n)
			time.Sleep(1 * time.Second)
			log.Println("work done:", n)
		case <- ctx.Done():
			log.Println("exit")
			goto EXIT
		}
	}
	EXIT:
		exit <- true
}

// 生产协程
func Producer(line chan int) {
	for i:=0; i < 10; i++ {
		line <- i
		time.Sleep(time.Second)
	}
}

3. 总结

程序的优雅退出时相当重要的,对于维护数据的完整性至关重要,也是一种很好的编码习惯;上面的示例提供了一种实现的方式,但是不同的运用场景可能需要更加细致的考虑;同时,也没办法处理kill -9这样暴力的关闭进程的场景;

你可能感兴趣的:(Golang及其项目管理)