likes
comments
collection
share

GO服务如何优雅的退出、重启

作者站长头像
站长
· 阅读数 5

简介

在服务端开发的时候,往往都会有一些关于服务器关闭、服务器重启之类的问题出现。这里简单介绍了 go服务 收到signal信号之后的关闭、重启操作。

参考:

优雅退出在Golang中的实现 (qq.com) Go语言WEB框架(Gin)详解

说明

go 中实现优雅的退出,主要使用了 os/signal 包,让程序能够接收到信号,并对信号执行一些操作。比如在linux下,也可以对一些信号进行捕捉,甚至改变信号所对应的行为。

退出部分的代码在退出标题里,其它代码只是我的一个测试环境,可以忽略。

这里我使用的是gin框架,并在gin框架中引入了os/signal包,实现对信号的处理(只做了部分信号的处理)。 并且有些信号不能被捕获,最常见的就是 kill -9 强杀,具体请看下最常见的信号列表。

GO服务如何优雅的退出、重启

代码实现

func (p *router) Run(host, port string) {
	//TODO 优化zap log日志,修改成自定义的zap 的logger

	//err := p.engine.Run(host + ":" + port)
	//if err != nil {
	//	//路由启动失败,终止程序
	//	panic(err)
	//}

	//下面所有代码(go1.8+)是为了优雅处理重启等动作
	srv := &http.Server{
		Addr:         host + ":" + port,
		Handler:      p.engine,
		ReadTimeout:  constant.ReadTimeoutT * time.Second,
		WriteTimeout: constant.WriteTimeoutT * time.Second,
	}

	go func() {
		// 监听请求
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	// 优雅Shutdown(或重启)服务
	// 有一点需要注意的是:这里设置的重启/关闭条件是 收到kill信号,即程序意外终止
	// 假如,我在这里用一个go携程,让它在2秒之后,panic程序,并且不进行recover恢复程序,那么panic会给进程发送一个os.Exit(2)(源码中可以看),
	// 这个是会被认为 程序主动退出 ,而非被kill 也就是进程不会收到 kill信号,也就无法重启/关闭了 (虽然panic让程序崩溃了)
	// 当注释掉下方go程中的defer的时候,程序直接panic,而不重启,反之则可以重启
	//go func() {
	//	defer Recover()
	//
	//	time.Sleep(2 * time.Second)
	//	panic("dangerous")
	//}()
	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt) // syscall.SIGKILL
	<-quit
	logger.Info("Shutdown Server...")

	// 10秒后,关闭context-即服务器于10秒后重启,或关闭(gin中的Context,也就是说客户端发送的一次请求,如果在10秒内没处理完,那么这个ctx将会失效,即无法在服务器重启前,成功处理客户端的请求)
	//TODO 设置一个context失效时间
	ctx, cancel := context.WithTimeout(context.Background(), constant.ServerRestart*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		logger.Fatalf("Server Shutdown: ", err)
	}
	select {
	case <-ctx.Done():
	}
	logger.Info("Server exiting...")
}

func Recover() {
	if p := recover(); p != nil {
		fmt.Println("p = ", p)
	}
}

如下,捕捉到了ctrl+c对应的信号之后,执行了退出操作,重启服务的时候也可以执行退出操作。

GO服务如何优雅的退出、重启