gin + endless 实现服务平滑重启

在 Gin 框架中结合 endless 库实现平滑重启(零停机更新),核心原理是通过 fork 子进程接管新请求,父进程处理完存量连接后退出。以下是完整实现方案及注意事项:


🛠️ 一、核心代码实现

go 复制代码
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) {
		// 模拟耗时操作(如数据库查询)
		time.Sleep(5 * time.Second)
		c.JSON(http.StatusOK, gin.H{"message": "pong"})
	})

	// 配置 endless 服务器
	server := endless.NewServer(":8080", router)
	server.ReadHeaderTimeout = 10 * time.Second
	server.WriteTimeout = 30 * time.Second

	// 监听信号并触发重启
	if err := server.ListenAndServe(); err != nil {
		if err != http.ErrServerClosed { // 非正常关闭时记录错误
			log.Fatalf("Server failed: %v", err)
		}
	}
	log.Println("Server exited gracefully")
}

⚙️ 二、平滑重启操作流程

  1. ​编译并启动服务​

    bash 复制代码
    go build -o app && ./app
    # 输出示例:Server started with PID 12345
  2. ​修改代码后重新编译​

    更新业务逻辑(如将 "pong" 改为 "pong v2"),重新编译:

    go 复制代码
    go build -o app
  3. ​触发平滑重启​

    向父进程发送 SIGHUP 信号(kill -1):

    bash 复制代码
    kill -1 12345
    • ​新进程​(如 PID 45678)启动并监听相同端口,处理新请求。
    • ​旧进程​ 继续处理存量请求(如休眠中的 /ping 请求),完成后自动退出。
  4. ​验证效果​

    • 重启前已发送的请求返回 "pong"
    • 重启后新请求返回 "pong v2",服务无中断。

📌 三、关键机制说明

​特性​ ​原理​ ​信号​
​优雅重启​ fork 子进程继承监听套接字,新旧进程并行处理请求 SIGHUP (kill -1)
​优雅关闭​ 父进程处理完存量连接后退出 SIGTERM/SIGINT
​强制终止​ 立即终止进程(不等待请求完成) SIGKILL (kill -9)

💡 ​​信号说明​​:

  • SIGHUP(默认):触发平滑重启
  • SIGTERM/SIGINT:优雅关闭服务
  • SIGUSR2:强制重启(HammerTime,不推荐)

⚠️ 四、生产环境注意事项

  1. ​进程管理工具兼容性​

    • 避免与 supervisorsystemd 直接管理 PID 的工具共用(重启后 PID 变化会导致误判服务崩溃)。
    • 替代方案:通过脚本监听服务状态,而非依赖 PID。
  2. ​容器化部署限制​

    Docker 默认单进程模型需调整:

    bash 复制代码
    # 使用 init 进程管理子进程
    ENTRYPOINT ["/sbin/tini", "--", "./app"]
  3. ​超时配置优化​

    ini 复制代码
    server.ReadHeaderTimeout = 5 * time.Second  // 防止慢连接攻击
    server.IdleTimeout = 120 * time.Second      // 长连接超时控制
  4. ​资源泄漏排查​

    • 父进程退出前确保释放数据库连接、文件句柄等资源。

    • 使用 defer 或注册清理函数:

      scss 复制代码
      server.BeforeShutdown = func() { 
          db.Close() 
      }

🔄 五、替代方案(不依赖 endless)

若需避免 PID 变化问题,可使用标准库实现:

scss 复制代码
// 参考网页8:通过 net.FileListener 传递套接字
l, _ := net.FileListener(os.NewFile(3, ""))
if os.Getenv("GRACEFUL_RESTART") == "true" {
    syscall.Kill(syscall.Getppid(), syscall.SIGTERM) // 终止父进程
}
http.Serve(l, router)

​缺点​​:需自行处理信号和进程同步,复杂度较高。


💎 总结

  • ​推荐场景​endless + Gin 适合快速实现平滑重启,适合非容器化或自定义进程管理的环境。
  • ​规避风险​ :容器中需搭配 tini,避免与 PID 敏感的工具集成。
  • ​验证要点​ :通过 time.Sleep 模拟长请求,观察新旧进程交替是否阻塞。

通过合理配置和测试,可确保 Go 服务更新时用户请求零中断,提升生产环境可靠性。测试时建议使用 siegewrk 模拟并发流量验证稳定性。

相关推荐
uzong4 小时前
技术故障复盘模版
后端
GetcharZp4 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程5 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研5 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi5 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy6 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack7 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9658 小时前
pip install 已经不再安全
后端
寻月隐君8 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github