gin如何实现热更新

什么是热更新?

一种不需要用户关闭应用或重新启动设备就能进行的软件更新技术**。**它可以快速地在线修复或升级应用程序的错误或功能,从而减少用户的等待时间并提高用户体验。

如何优雅停止服务?

Go 1.8版本之后, http.Server 内置的Shutdown() 方法就支持优雅地关机,具体示例如下:

// +build go1.8

package main

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

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

func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		time.Sleep(5 * time.Second)
		c.String(http.StatusOK, "Welcome Gin Server")
	})

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

	go func() {
		// 开启一个goroutine启动服务
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	// 等待中断信号来优雅地关闭服务器,为关闭服务器操作设置一个5秒的超时
	quit := make(chan os.Signal, 1) // 创建一个接收信号的通道
	// kill 默认会发送 syscall.SIGTERM 信号
	// kill -2 发送 syscall.SIGINT 信号,我们常用的Ctrl+C就是触发系统SIGINT信号
	// kill -9 发送 syscall.SIGKILL 信号,但是不能被捕获,所以不需要添加它
	// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)  // 此处不会阻塞
	<-quit  // 阻塞在此,当接收到上述两种信号时才会往下执行
	log.Println("Shutdown Server ...")
	// 创建一个5秒超时的context
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	// 5秒内优雅关闭服务(将未处理完的请求处理完再关闭服务),超过5秒就超时退出
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server Shutdown: ", err)
	}

	log.Println("Server exiting")
}

还是一样的步骤,先编译生成windows或者linux的执行程序,这里比较简单,就直接在windows上进行验证了,详细验证步骤:

(1)编译项目在windows上的执行程序

go build

(2)启动服务

C:\Users\leell\go\src\gin-test>gin-test.exe
[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.

(3)浏览器访问地址,由于我在路由那里有5s的睡眠,不会立即返回

(4)在终端迅速 执行Ctrl+C命令给程序发送syscall.SIGINT信号

此时程序并不立即退出而是等我们第(3)步的响应返回之后再退出,从而实现优雅关机。

从上面的图片可以看出Shutdown Server ...

(5)查看关闭结果

如何优雅地重启?

优雅关机实现了,那么该如何实现优雅重启呢?下面的内容摘抄自网络,由于go自带graceful,下面留作本文展现

我们可以使用 fvbock/endless 来替换默认的 ListenAndServe启动服务来实现, 示例代码如下:

package main

import (
	"log"
	"net/http"
	"time"

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

func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		time.Sleep(5 * time.Second)
		c.String(http.StatusOK, "hello gin!")
	})
	// 默认endless服务器会监听下列信号:
	// syscall.SIGHUP,syscall.SIGUSR1,syscall.SIGUSR2,syscall.SIGINT,syscall.SIGTERM和syscall.SIGTSTP
	// 接收到 SIGHUP 信号将触发`fork/restart` 实现优雅重启(kill -1 pid会发送SIGHUP信号)
	// 接收到 syscall.SIGINT或syscall.SIGTERM 信号将触发优雅关机
	// 接收到 SIGUSR2 信号将触发HammerTime
	// SIGUSR1 和 SIGTSTP 被用来触发一些用户自定义的hook函数
	if err := endless.ListenAndServe(":8080", router); err!=nil{
		log.Fatalf("listen: %s\n", err)
	}

	log.Println("Server exiting")
}

如何验证优雅重启的效果呢?

我们通过执行kill -1 pid命令发送syscall.SIGINT来通知程序优雅重启,具体做法如下:

  1. 打开终端,go build -o graceful_restart编译并执行./graceful_restart,终端输出当前pid(假设为43682)
  2. 将代码中处理请求函数返回的hello gin!修改为hello q1mi!,再次编译go build -o graceful_restart
  3. 打开一个浏览器,访问127.0.0.1:8080/,此时浏览器白屏等待服务端返回响应。
  4. 在终端迅速 执行kill -1 43682命令给程序发送syscall.SIGHUP信号
  5. 等第3步浏览器收到响应信息hello gin!后再次访问127.0.0.1:8080/会收到hello q1mi!的响应。
  6. 在不影响当前未处理完请求的同时完成了程序代码的替换,实现了优雅重启。

但是需要注意的是,此时程序的PID变化了,因为endless 是通过fork子进程处理新请求,待原进程处理完当前请求后再退出的方式实现优雅重启的。所以当你的项目是使用类似supervisor的软件管理进程时就不适用这种方式了。

相关推荐
SoraLuna7 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
追风林7 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
yqcoder7 小时前
mac 安装 nodemon
macos
一ge科研小菜鸡7 小时前
macOS开发环境配置与应用开发(详细讲解)
macos
hairenjing11237 小时前
使用 Mac 数据恢复从 iPhoto 图库中恢复照片
windows·stm32·嵌入式硬件·macos·word
青花瓷8 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
zorchp14 小时前
在 MacOS 上跑 kaldi
macos·kaldi
德育处主任14 小时前
Mac和安卓手机互传文件(ADB)
android·macos
RationalDysaniaer14 小时前
Gin入门笔记
笔记·gin
千年死缓14 小时前
gin中间件
中间件·gin