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")
}
AI 代码解读

如何验证优雅重启?

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

  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 认为服务已经崩溃。

总结

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

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

目录
打赏
0
0
3
1
116
分享
相关文章
|
4天前
|
Golang | Gin:net/http与Gin启动web服务的简单比较
总的来说,`net/http`和 `Gin`都是优秀的库,它们各有优缺点。你应该根据你的需求和经验来选择最适合你的工具。希望这个比较可以帮助你做出决策。
51 35
PHP 日志系统的最佳搭档:一个 Go 写的远程日志收集服务
为了不再 SSH 上去翻日志,我写了个 Go 小脚本,用来接收远程日志。PHP 负责记录日志,Go 负责存储和展示,按天存储、支持 API 访问、可远程管理,终于能第一时间知道项目炸了。
49 10
FastAPI与Selenium:打造高效的Web数据抓取服务 —— 采集Pixabay中的图片及相关信息
本文介绍了如何使用FastAPI和Selenium搭建RESTful接口,访问免版权图片网站Pixabay并采集图片及其描述信息。通过配置代理IP、User-Agent和Cookie,提高爬虫的稳定性和防封禁能力。环境依赖包括FastAPI、Uvicorn和Selenium等库。代码示例展示了完整的实现过程,涵盖代理设置、浏览器模拟及数据提取,并提供了详细的中文注释。适用于需要高效、稳定的Web数据抓取服务的开发者。
160 15
FastAPI与Selenium:打造高效的Web数据抓取服务 —— 采集Pixabay中的图片及相关信息
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
136 7
|
6月前
|
Go Web服务中如何优雅关机?
在构建 Web 服务时,优雅关机是一个关键的技术点,它确保服务关闭时所有正在处理的请求都能顺利完成。本文通过一个简单的 Go 语言示例,展示了如何使用 Gin 框架实现优雅关机。通过捕获系统信号和使用 `http.Server` 的 `Shutdown` 方法,我们可以在服务关闭前等待所有请求处理完毕,从而提升用户体验,避免数据丢失或不一致。
70 1
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
3月前
|
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
Go 语言入门指南:切片
|
3月前
|
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
本文探讨了如何利用 Go 语言中的 Bloom Filter 算法提升公司局域网管理系统的性能。Bloom Filter 是一种高效的空间节省型数据结构,适用于快速判断元素是否存在于集合中。文中通过具体代码示例展示了如何在 Go 中实现 Bloom Filter,并应用于局域网的 IP 访问控制,显著提高系统响应速度和安全性。随着网络规模扩大和技术进步,持续优化算法和结合其他安全技术将是企业维持网络竞争力的关键。
70 2
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
|
3月前
|
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
111 20

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等