Go Web服务中如何优雅平滑重启?

简介: 在生产环境中,服务升级时如何确保不中断当前请求并应用新代码是一个挑战。本文介绍了如何使用 Go 语言的 `endless` 包实现服务的优雅重启,确保在不停止服务的情况下完成无缝升级。通过示例代码和测试步骤,详细展示了 `endless` 包的工作原理和实际应用。

在生产环境中,当我们需要对正在运行的服务进行升级时,如何确保不影响当前未处理完的请求,同时又能应用新的代码,是个极具挑战性的问题。

传统的做法通常是停止当前服务,部署新代码后再重启服务,但这种方式会导致正在处理的请求被强制中断,用户体验会受到很大的影响。

在这篇文章中,我将带大家一起探索如何在 Go 语言中通过使用 endless 包来实现服务的优雅重启,即在不影响当前正在处理的请求的情况下,完成服务的无缝升级。

什么是优雅重启?

优雅重启的核心思想是:在服务启动新的进程处理新请求的同时,允许旧的进程继续完成其手头未完成的工作,然后再优雅地退出。这种方式可以确保服务在升级的过程中不会出现中断,提升用户体验的同时,也降低了在服务切换过程中的风险。

实现优雅重启的代码示例

下面的代码演示了如何使用 endless 包来实现 Gin 服务的优雅重启。

package main

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

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

func main() {
   
    router := gin.Default()
    router.GET("/ping", func(c *gin.Context) {
   
        // 模拟程序处理请求需要 5 秒
        time.Sleep(5 * time.Second)
        c.JSON(http.StatusOK, gin.H{
   
            "message": "pong",
        })
    })

    // 默认 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.Printf("Server err: %v\n", err)
    }

    log.Println("Server exiting")
}

如何验证优雅重启?

为了验证这个实现,我们可以通过以下步骤进行测试:

  1. 打开终端,执行 go build -o gin_graceful_restart gin_graceful_restart.go 编译程序,然后执行 ./gin_graceful_restart 启动服务。终端会输出当前服务的 PID(例如:[pid] 12345)。

  2. 修改代码中的 /ping 接口的响应内容,比如将 pong 修改为 pong1

  3. 再次执行 go build -o gin_graceful_restart gin_graceful_restart.go 编译程序。

  4. 在浏览器中访问 http://127.0.0.1:8080/ping,此时浏览器会等待服务返回响应(由于接口模拟了 5 秒的延迟)。

  5. 在另一个终端中执行 kill -1 12345 命令,向服务发送 syscall.SIGHUP 信号,12345 为第一步中的 PID。

  6. 依旧在第 4 步的浏览器中等待,等响应到 pong 信息之后,再次刷新页面,你会发现响应内容变成了 pong1,这意味着服务已经应用了新的代码,同时之前的请求也得到了正确的处理。

Endless 的工作原理

endless 包实现优雅重启的原理非常简单:它会 fork 一个新的子进程来处理新的请求,而旧的进程则继续处理已经接收的请求。当旧的进程处理完所有的请求后才会退出。因此,虽然 PID 发生了变化,但服务依旧保持了无缝衔接。

值得注意的是,由于 endless 的这种机制,当你的项目是通过类似 supervisor 的软件来管理进程时,这种方式就不再适用了。因为 supervisor 会根据 PID 来管理进程,而在优雅重启过程中 PID 是会变化的,这会导致 supervisor 认为服务已经崩溃。

总结

在实际的生产环境中,优雅重启是非常实用的一项技术,它可以帮助我们在不影响用户体验的前提下,对服务进行升级和维护。

如果你也在开发一个长期运行的服务,希望本文的介绍能对你有所帮助,让你的服务更加健壮和可靠。

相关文章
|
2月前
|
XML JSON 安全
Web服务是通过标准化的通信协议和数据格式
【10月更文挑战第18天】Web服务是通过标准化的通信协议和数据格式
173 69
|
1月前
|
JSON Go UED
Go Web服务中如何优雅关机?
在构建 Web 服务时,优雅关机是一个关键的技术点,它确保服务关闭时所有正在处理的请求都能顺利完成。本文通过一个简单的 Go 语言示例,展示了如何使用 Gin 框架实现优雅关机。通过捕获系统信号和使用 `http.Server` 的 `Shutdown` 方法,我们可以在服务关闭前等待所有请求处理完毕,从而提升用户体验,避免数据丢失或不一致。
27 1
|
1月前
|
XML 安全 PHP
PHP与SOAP Web服务开发:基础与进阶教程
本文介绍了PHP与SOAP Web服务的基础和进阶知识,涵盖SOAP的基本概念、PHP中的SoapServer和SoapClient类的使用方法,以及服务端和客户端的开发示例。此外,还探讨了安全性、性能优化等高级主题,帮助开发者掌握更高效的Web服务开发技巧。
|
2月前
|
XML JSON 安全
定义Web服务
【10月更文挑战第18天】定义Web服务
79 12
|
3天前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
30 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
23天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
36 7
|
22天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
23天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
97 71
|
22天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
102 67
|
25天前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
93 62