Go Web 框架 Gin 如何实现优雅退出?

1,121 阅读2分钟

我在《Go 标准库 net/http 如何实现优雅退出?》 一文中讲解了 net/http 实现的 HTTP Server 如何优雅退出,又在《深挖 Go 标准库 net/http 源码,来看看优雅退出到底是如何实现的?》一文讲解了优雅退出源码是如何实现的。

Gin 的优雅退出

但在工作中,我们开发 Go Web 程序时,往往不会直接使用 net/http 包,而是引入一个第三方库或框架。其中 Gin 作为 Go 生态中最为流行的 Web 库,我觉得有必要讲解一下在 Gin 中如何进行优雅退出。

不过,学习了 net/http 的优雅退出,实际上 Gin 框架的优雅退出是一样的,因为 Gin 只是一个路由库,提供 HTTP Server 能力的还是 net/http

Gin 框架中的优雅退出示例代码如下:

package main

import (
	"context"
	"errors"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()

	router.GET("/sleep", func(c *gin.Context) {
		duration, err := time.ParseDuration(c.Query("duration"))
		if err != nil {
			c.String(http.StatusBadRequest, err.Error())
			return
		}

		time.Sleep(duration)
		c.String(http.StatusOK, "Welcome Gin Server")
	})

	srv := &http.Server{
		Addr:    ":8000",
		Handler: router,
	}

	go func() {
		// 服务连接
		if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
			// Error starting or closing listener:
			log.Fatalf("HTTP server ListenAndServe: %v", err)
		}
		log.Println("Stopped serving new connections")
	}()

	// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	<-quit
	log.Println("Shutdown Server...")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// We received an SIGINT/SIGTERM signal, shut down.
	if err := srv.Shutdown(ctx); err != nil {
		// Error from closing listeners, or context timeout:
		log.Printf("HTTP server Shutdown: %v", err)
	}
	log.Println("HTTP server graceful shutdown completed")
}

可以发现,在 Gin 框架中实现优雅退出代码与我们在 net/http 包中的实现没什么不同。

只不过我们在实例化 http.Server 对象时,将 *gin.Engine 作为了 Handler 复制给 Handler 属性:

srv := &http.Server{
    Addr:    ":8000",
    Handler: router,
}

执行示例程序测试优雅退出,得到如下输出:

$ go build -o main main.go && ./main
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /sleep                    --> main.main.func1 (3 handlers)
^C2024/08/22 09:23:36 Shutdown Server...
2024/08/22 09:23:36 Stopped serving new connections
[GIN] 2024/08/22 - 09:23:39 | 200 |  5.001282167s |       127.0.0.1 | GET      "/sleep?duration=5s"
2024/08/22 09:23:39 HTTP server graceful shutdown completed
$ curl "http://localhost:8000/sleep?duration=5s"
Welcome Gin Server

这里同样在处理请求的过程中,按下 Ctrl + C,根据日志可以发现,Gin 示例代码中的优雅退出没有问题。

Gin 框架文档 也提到了在 Go 1.8 版本之前可以使用如下几个第三方库实现的优雅退出替代方案:

  • manners:可以优雅关机的 Go Http 服务器。
  • graceful:Graceful 是一个 Go 扩展包,可以优雅地关闭 http.Handler 服务器。
  • grace:Go 服务器平滑重启和零停机时间部署。

当然我们使用的是 Go 1.8 以上版本,可以不需要这些库。因为 net/http 已经提供了原生的优雅退出方案,所以几乎用不到它们,感兴趣的可以自行研究下。

NOTE: 可以参阅 Gin 完整的 graceful-shutdown 示例。

联系我