优雅退出:信号、Server 关闭与资源清理
进程被 kill、Ctrl+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.Exit:gproc.Listen的回调全部完成后进程自然退出,强杀反而破坏优雅性。 - 平滑重启不要用
go run/IDE 运行测试,父进程会干扰新老进程状态交换(官方文档明确提示)。 - 容器/编排平台的
SIGTERM+ 宽限期(grace period)机制要配合:回调逻辑别超过平台给的宽限时间,否则会被SIGKILL兜底强杀。
小结
优雅退出的标准动作:gproc.AddSigHandlerShutdown 注册清理回调 → 关闭业务资源 → s.Shutdown() → gproc.Listen() 收尾。开启 graceful 后还能让存量请求平滑跑完。信号清单与平台差异以官方文档 goframe.org 为准。