优雅退出:信号、Server 关闭与资源清理

进程被 killCtrl+C、发布平台滚动重启时,如果直接强杀,可能丢掉正在写的数据、中断进行中的请求。优雅退出要解决的是:收到退出信号后,先停止接收新请求,让存量请求处理完,再统一释放资源后退出。GoFrame 从三个层面支持这件事:g.Server 的关闭方法、gproc 的统一信号监听、平滑重启特性。

基本概念

  • 进程退出信号:Linux 下常见 SIGINT(Ctrl+C)、SIGTERM(kill 默认)、SIGQUIT 等。
  • ghttp.Server 提供 s.Shutdown()(关闭服务)与 s.Restart()(重启)方法;s.Run() 会阻塞运行直到服务关闭。
  • 官方“平滑重启特性”说明:通过 graceful 配置(或 s.SetGraceful(true),v2.7.4+)可开启平滑重启,*nix 下用 SIGUSR1 触发重启、SIGINT/SIGTERM 等触发关闭;Windows 下仅支持完整重启。
  • 多个组件各自监听信号会互相打架,官方推荐用 gproc 统一注册与回调。

Server 的关闭与恢复

package main

import (
    "github.com/gogf/gf/v2/frame/g"
    "github.com/gogf/gf/v2/net/ghttp"
)

func main() {
    s := g.Server()
    s.BindHandler("/", func(r *ghttp.Request) {
        r.Response.Write("hello")
    })
    s.SetPort(8199)

    // Run 阻塞;收到关闭信号或调用 Shutdown 后退出
    s.Run()
    g.Log().Info(nil, "server exited gracefully")
}

任意位置都能通过单例 g.Server() 拿到同一对象并调用 Shutdown() 触发关闭;开启平滑特性后,关闭过程会先等正在执行的请求处理完成(日志会出现 “server shutting down / all servers shutdown”)。

gproc 统一信号处理

业务里常有不属于 HTTP 的资源:定时任务、消息队列消费者、自建连接池。官方 gproc 组件提供 AddSigHandlerShutdown 统一注册退出回调,全部回调执行完毕后进程才真正退出:

package main

import (
    "fmt"
    "os"

    "github.com/gogf/gf/v2/frame/g"
    "github.com/gogf/gf/v2/os/gproc"
)

func main() {
    s := g.Server()
    // ... 注册路由、端口 ...

    // 退出信号(SIGINT/SIGQUIT/SIGKILL/SIGTERM/SIGABRT)触发时依次执行回调
    gproc.AddSigHandlerShutdown(
        func(sig os.Signal) {
            // 1. 停掉定时任务、MQ 消费者等业务资源
            fmt.Println("cleanup cron jobs:", sig)
        },
        func(sig os.Signal) {
            // 2. 最后关闭 HTTP Server
            if err := s.Shutdown(); err != nil {
                fmt.Println("shutdown server error:", err)
            }
        },
    )
    // Listen 阻塞监听信号并执行回调;全部回调结束后进程平滑退出
    gproc.Listen()
}

gproc.AddSigHandler(sig, handler) 可以针对具体信号注册;官方文档对比过标准库写法:每个组件各起一个 goroutine 监听会导致“进程已退、组件没退完”,gproc 的顺序回调机制则保证各组件有序析构。

资源清理清单

  • HTTP Server:s.Shutdown()
  • 定时任务:逐个 gcron.Remove(name),或 v2.8 起的 StopGracefully 等待在跑任务结束。
  • 链路追踪上报器:执行初始化返回的 shutdown()(见链路追踪章节)。
  • 数据库/Redis 连接池由框架托管,进程退出时随进程释放;如使用第三方长连接客户端,则在回调里显式关闭。

注意点

  • 回调里不要调用 os.Exitgproc.Listen 的回调全部完成后进程自然退出,强杀反而破坏优雅性。
  • 平滑重启不要用 go run/IDE 运行测试,父进程会干扰新老进程状态交换(官方文档明确提示)。
  • 容器/编排平台的 SIGTERM + 宽限期(grace period)机制要配合:回调逻辑别超过平台给的宽限时间,否则会被 SIGKILL 兜底强杀。

小结

优雅退出的标准动作:gproc.AddSigHandlerShutdown 注册清理回调 → 关闭业务资源 → s.Shutdown()gproc.Listen() 收尾。开启 graceful 后还能让存量请求平滑跑完。信号清单与平台差异以官方文档 goframe.org 为准。

笔记加载中…