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

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

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 使用最为简单,重启快速,是实现热重启的好选择。

目录
相关文章
|
9天前
|
存储 应用服务中间件 文件存储
Ngnix服务器版本升级需求分析,如何不停止Ngnix服务进行升级
Ngnix服务器版本升级需求分析,如何不停止Ngnix服务进行升级
|
11天前
|
缓存 小程序
【微信小程序-原生开发】启动时自动升级更新到最新版本
【微信小程序-原生开发】启动时自动升级更新到最新版本
13 0
|
1月前
|
运维 JavaScript Serverless
Serverless 应用引擎产品使用合集之php工程已经部署,可以正常访问数据库,静态资源样式无法正常加载,要怎么配置
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
Serverless 应用引擎产品使用合集之php工程已经部署,可以正常访问数据库,静态资源样式无法正常加载,要怎么配置
|
2月前
|
缓存 弹性计算 Serverless
Serverless 应用引擎操作报错问题之正常运行了两个月,今天更新小功能,结果前后端都报这个错误如何解决
Serverless 应用引擎(SAE)是阿里云提供的Serverless PaaS平台,支持Spring Cloud、Dubbo、HSF等主流微服务框架,简化应用的部署、运维和弹性伸缩。在使用SAE过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
2月前
|
Serverless 应用服务中间件 开发者
Serverless应用引擎问题之源码部署关掉如何解决
Serverless部署是指将应用程序部署到无服务器架构中,该架构允许开发者专注于代码而无需关心底层服务器的运行和维护;针对Serverless部署过程中可能遇到的挑战,本合集提供全面的指南和最佳实践,帮助开发者顺利实现应用的无服务器化部署。
|
7月前
|
程序员 API 开发者
自动化脚本如何编写?打算写个自动发布文章的脚本教程
作为一名程序员/开发者,我们经常需要处理重复性的任务,比如发布文章到多个媒体平台。为了提高效率,我们可以编写自动化脚本来完成这些任务。本文将介绍如何使用万媒易发多平台内容同步助手来自动发布文章。
|
存储 安全 开发者
企业用WordPress开发插件需要定期更新吗?
企业使用WordPress开发插件时,需要定期更新插件以确保插件的安全性和兼容性。 插件开发者会定期发布更新来修复漏洞、改进功能和确保兼容性。如果插件没有及时更新,那么它可能会存在漏洞或与最新版本的WordPress或其他插件不兼容,从而导致安全和性能问题。除了定期更新插件以确保安全性和兼容性,企业在使用WordPress插件时还有以下一些最佳实践:
|
PHP 开发工具 数据库
Yii2 修改框架入口增加配置适应开发生产环境
Yii2 修改框架入口增加配置适应开发生产环境
102 0
Yii2 修改框架入口增加配置适应开发生产环境
|
负载均衡 应用服务中间件 开发工具
easyswoole实现线上更新代码
easyswoole实现线上更新代码
254 0
easyswoole实现线上更新代码
|
Web App开发 运维 前端开发
Vite + whistle:一劳永逸的开发环境代理方案
开发环境面临的跨域问题是每个开发者都会遇到的,有什么方法能“一劳永逸”呢?本文对比了多种方案,也植入到企业项目中进行尝试取得不错的效果,希望在跨域问题上能帮到你。
Vite + whistle:一劳永逸的开发环境代理方案