热更新最佳实践,网站无感知重启方法大全

简介: 热更新最佳实践,网站无感知重启方法大全

1. 热重启的意义

在 Web 开发中,经常需要更新业务,每次更新代码后,都需要重启应用程序才能生效。

但是每次重启都会中断当前的服务,导致已经连接的请求失败,用户会感受到服务中断。

这就要求服务能够在不中断的情况下重启应用并加载新代码,这就是热重启。

热重启的意义主要体现在:

不中断服务,确保系统可用性

热重启可以在不停止服务的情况下生效新代码,确保系统可以正常提供服务,避免因重启而造成的可用性中断。

实时加载新代码,快速迭代

通过热重启可以跳过重启应用的步骤,直接生效新代码,大大提高开发和迭代的效率。

简化部署,无缝上线

热重启可以作为零停机部署的一种手段,简化繁琐的部署流程,实现应用的无缝上线。


 

2. Go 语言中的热重启方法

Go 语言实现热重启主要有以下几种方法

net/http 包的 Server.ListenAndServe 函数

ListenAndServe 函数可以直接创建 HTTP 服务。可通过在另一个 goroutine 中重新调用 ListenAndServe 来重启服务。

gin 等 Web 框架的方法

流行的 Web 框架如 gin 都内置了热重启机制,可以基于框架实现轻松的热重启。

fresh、enduro/restart 等第三方库

这些优秀的第三方库可以让开发者只通过简单的配置就能启用热重启,使用非常方便。

下面先介绍 ListenAndServe 函数实现热重启的方法,然后介绍基于 gin 框架的热重启, 最后给出基于 fresh 库的热重启示例。


 

3. ListenAndServe 函数实现热重启

3.1 创建 HTTP 服务

需创建一个基本的 HTTP 服务。以下是一个简单的例子:


package main
import (  "fmt"  "net/http")
func main() {  http.HandleFunc("/", func(w http.ResponseWriter,   r *http.Request) {    fmt.Fprintf(w, "Hello, World!")  })
  http.ListenAndServe(":8080", nil)}

3.2 另一个 goroutine 中重启服务

为了实现热重启,可在一个新的 goroutine 中监听新的端口:



package main
import (  "fmt"  "net/http"  "os")
func main() {  http.HandleFunc("/", func(w http.ResponseWriter,   r *http.Request) {    fmt.Fprintf(w, "Hello, World!")  })
  go func() {    http.ListenAndServe(":8081", nil)  }()
  http.ListenAndServe(":8080", nil)}

3.3 停止旧的服务

为了避免端口冲突,可在新的 goroutine 中监听新的端口后,关闭旧的服务:


package main
import (  "fmt"  "net/http"  "os"  "os/signal"  "syscall")
func main() {  http.HandleFunc("/", func(w http.ResponseWriter,   r *http.Request) {      fmt.Fprintf(w, "Hello, World!")      })
  // 创建信号通道  sigCh := make(chan os.Signal, 1)  signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
  // 启动新的服务  go func() {    http.ListenAndServe(":8081", nil)  }()
  // 等待信号  <-sigCh}


 

4. Gin 框架实现热重启

4.1 LoadHTMLGlobles

使用 Gin 框架时,可利用其提供的 LoadHTMLGlob 函数来实现热重启。该函数用于加载 HTML 模板文件。


package main
import (  "github.com/gin-gonic/gin")
func main() {  router := gin.Default()
  // 加载HTML模板文件  router.LoadHTMLGlob("templates/*")
  router.GET("/", func(c *gin.Context) {    c.HTML(200, "index.html", gin.H{})  })
  // 启动服务器  router.Run(":8080")}

4.2 Run 方法

Run 方法启动 Gin 服务器,该方法会自动处理热重启


package main
import (  "github.com/gin-gonic/gin")
func main() {  router := gin.Default()
  router.LoadHTMLGlob("templates/*")
  router.GET("/", func(c *gin.Context) {    c.HTML(200, "index.html", gin.H{})  })
  // 使用Run方法启动服务器,支持热重启  router.Run(":8080")}

4.3 信号处理函数

为了更加优雅地处理热重启,可通过信号处理函数来实现


package main
import (  "os"  "os/signal"  "syscall"
  "github.com/gin-gonic/gin")
func main() {  router := gin.Default()
  router.LoadHTMLGlob("templates/*")
  router.GET("/", func(c *gin.Context) {    c.HTML(200, "index.html", gin.H{})  })
  // 监听系统信号  sigCh := make(chan os.Signal, 1)  signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
  go func() {    // 阻塞,等待信号    <-sigCh
    // 执行一些清理工作,然后退出    // 例如关闭数据库连接等    os.Exit(0)  }()
  // 使用Run方法启动服务器,支持热重启  router.Run(":8080")}


 

5. Fresh 库实现热重启

Fresh 是一个专门用于热重启的第三方 Go 库。

5.1 Fresh 的工作原理

Fresh 会启动两个进程,一个父进程和一个子进程。

父进程维护一个 websocket 服务,用于和子进程通信。

子进程会调用运行的 Go web 应用。

当修改了代码,就可以通过 websocket 给子进程发送信号,子进程接收到信号后会重启的应用。

这样就实现了应用的热重启。

5.2 如何使用 Fresh

使用 Fresh 非常简单,只需要在 main 函数调用 Fresh 方法:


import "github.com/gravityblast/fresh"
func main() {  fresh.Fresh(rootHandler)}
func rootHandler(w http.ResponseWriter, r *http.Request) {  //...}

然后在代码修改时按 Ctrl+C,就会触发重启了。

5.3 Fresh 比自定义实现的优点

相比于自定义的热重启实现,Fresh 有以下优点

使用简单,只需要引入库和添加一行代码。

重启快速,平均重启时间 60-100ms。

自动检查代码修改,无需其他操作。


 

6. 问题及解决方案

虽然上面介绍的几种热重启机制可以零停机应用新代码,但是仍有一些需要注意的问题。

6.1 数据状态、连接处理

热重启后程序会重新初始化,需要妥善处理状态和连接的维护,防止数据丢失或连接中断。

解决方法是在停止服务前,保存必要的状态,并等待当前连接处理完再重启,重启后尽量做好状态恢复,重建连接等操作。

6.2 最小化中断时间

要使热重启对服务的影响最小化,需确保重启过程尽可能快,中断时间维持在毫秒级。

解决方法是仅重启必要的组件,其他组件如数据库等保持运行;同时可以通过配置进行预加载加速重启过程。


 

总结

热重启的意义在于不中断服务,实时加载新代码,简化上线部署。

Go 语言中实现热重启的主要方式有:

net/http 的 ListenAndServe 函数

Web 框架如 gin 的内置机制

第三方库 fresh

各实现方式的优缺点:

ListenAndServe 方式可以自行控制逻辑, but 需要自己实现较复杂的控制流程。

Gin 内置的热重启简单易用,但依赖了框架。


Fresh 使用最为简单,重启快速,是实现热重启的好选择。

目录
相关文章
|
6月前
|
关系型数据库 MySQL 测试技术
ChaosBlade常见问题之页面没有机器列表权限不能执行故障注入如何解决
ChaosBlade 是一个开源的混沌工程实验工具,旨在通过模拟各种常见的硬件、软件、网络、应用等故障,帮助开发者在测试环境中验证系统的容错和自动恢复能力。以下是关于ChaosBlade的一些常见问题合集:
|
3月前
|
JSON Android开发 数据格式
Android项目架构设计问题之在远端动态配置中添加相应配置如何解决
Android项目架构设计问题之在远端动态配置中添加相应配置如何解决
28 0
|
4月前
|
存储 应用服务中间件 文件存储
Ngnix服务器版本升级需求分析,如何不停止Ngnix服务进行升级
Ngnix服务器版本升级需求分析,如何不停止Ngnix服务进行升级
|
5月前
|
运维 JavaScript Serverless
Serverless 应用引擎产品使用合集之php工程已经部署,可以正常访问数据库,静态资源样式无法正常加载,要怎么配置
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
Serverless 应用引擎产品使用合集之php工程已经部署,可以正常访问数据库,静态资源样式无法正常加载,要怎么配置
|
4月前
|
缓存 小程序
【微信小程序-原生开发】启动时自动升级更新到最新版本
【微信小程序-原生开发】启动时自动升级更新到最新版本
66 0
|
6月前
|
缓存 弹性计算 Serverless
Serverless 应用引擎操作报错问题之正常运行了两个月,今天更新小功能,结果前后端都报这个错误如何解决
Serverless 应用引擎(SAE)是阿里云提供的Serverless PaaS平台,支持Spring Cloud、Dubbo、HSF等主流微服务框架,简化应用的部署、运维和弹性伸缩。在使用SAE过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
6月前
|
算法 安全 Java
服务器启动 SpringBoot 后访问特别慢的解决方案
服务器启动 SpringBoot 后访问特别慢的解决方案
122 1
|
6月前
|
Serverless 应用服务中间件 开发者
Serverless应用引擎问题之源码部署关掉如何解决
Serverless部署是指将应用程序部署到无服务器架构中,该架构允许开发者专注于代码而无需关心底层服务器的运行和维护;针对Serverless部署过程中可能遇到的挑战,本合集提供全面的指南和最佳实践,帮助开发者顺利实现应用的无服务器化部署。
|
前端开发 API 开发者
热更新原理
热更新原理
168 0
|
前端开发 应用服务中间件 nginx
项目实战典型案例28——生产环境nginx限制上传大小带来的灾难
项目实战典型案例28——生产环境nginx限制上传大小带来的灾难
165 0