什么是热更新?
一种不需要用户关闭应用或重新启动设备就能进行的软件更新技术。它可以快速地在线修复或升级应用程序的错误或功能,从而减少用户的等待时间并提高用户体验。
如何优雅停止服务?
Go 1.8版本之后, http.Server 内置的Shutdown() 方法就支持优雅地关机,具体示例如下:
// +build go1.8package mainimport ("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 信号转发给quitsignal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞<-quit // 阻塞在此,当接收到上述两种信号时才会往下执行log.Println("Shutdown Server ...")// 创建一个5秒超时的contextctx, 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 mainimport ("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
来通知程序优雅重启,具体做法如下:
- 打开终端,
go build -o graceful_restart
编译并执行./graceful_restart
,终端输出当前pid(假设为43682) - 将代码中处理请求函数返回的
hello gin!
修改为hello q1mi!
,再次编译go build -o graceful_restart
- 打开一个浏览器,访问
127.0.0.1:8080/
,此时浏览器白屏等待服务端返回响应。 - 在终端迅速执行
kill -1 43682
命令给程序发送syscall.SIGHUP
信号 - 等第3步浏览器收到响应信息
hello gin!
后再次访问127.0.0.1:8080/
会收到hello q1mi!
的响应。 - 在不影响当前未处理完请求的同时完成了程序代码的替换,实现了优雅重启。
但是需要注意的是,此时程序的PID变化了,因为endless
是通过fork
子进程处理新请求,待原进程处理完当前请求后再退出的方式实现优雅重启的。所以当你的项目是使用类似supervisor
的软件管理进程时就不适用这种方式了。
参考文章:
https://www.fansimao.com/937614.html