跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Golang怎么实现优雅重启_Golang如何不中断服务实现热升级和重启【进阶】

根本办法是旧进程等待请求自然结束、新进程复用监听socket并接管新连接,核心靠net.Listener传递与SIGUSR2信号切换,必须用http.Server.Shutdown()配合超时上下文,避免RST中断和goroutine泄漏。 Go 服务如何避免重启时丢请求 根本办法不是“不重启”,而是让旧进程等正在处理的请求自然结束,新进程提前监听端口并接管新连接。核心靠
net.Listener
的传递和
syscall.SIGUSR2
(或
SIGHUP
)信号触发切换。 常见错误是直接
os.Exit(0)
或杀掉进程,导致 TCP 连接被 RST 中断,客户端收到 “connection reset” 或超时;另一类是没控制好旧 goroutine 的生命周期,导致内存泄漏或 panic。 必须用
http.Server.Shutdown()
配合上下文超时,不能只靠
server.Close()
新进程启动前,要通过文件描述符继承(
os.NewFile()
+
net.FileListener()
)复用父进程的监听 socket,否则会报
address already in use
旧进程在
Shutdown()
返回后才真正退出,期间拒绝新连接但继续服务已有连接 使用
graceful
库还是手写
Shutdown
逻辑 手写更可控,依赖少;
graceful
类库(如
facebookgo/grace
或
tylerb/graceful
)已过时或停止维护,且多数不兼容 Go 1.8+ 的原生
Shutdown()
。现在推荐直接用标准库。 关键差异在信号处理时机和 listener 传递方式:旧进程收到
SIGUSR2
后 fork 新进程,把 listener fd 传过去;新进程用
os.NewFile(uintptr(fd), "")
恢复 listener,再调
net.FileListener()
。 立即学习 “ go语言免费学习笔记(深入) ”; Go 1.8+ 必须用
http.Server.Shutdown(ctx)
,它会等待活跃请求完成,而
Close()
是立即关闭 超时时间建议设为 30–60 秒,太短可能强制中断长请求,太长拖慢升级节奏 注意
context.WithTimeout
的 parent context 不要带 cancel,否则可能提前终止 shutdown 流程
SIGUSR2
触发重启时子进程拿不到监听端口 典型错误是子进程没正确继承文件描述符,或者父进程没在 fork 前调用
listener.(*net.TCPListener).File()
获取 fd 并传给子进程。Linux 下 fd 默认不继承,需显式设置
fd.CloseOnExec = false
(但 Go 的
File()
已自动处理)。 另一个坑是子进程启动后立刻调
listener.Accept()
,但此时父进程还没完全释放 socket,导致
bind: address already in use
—— 实际上是因为 socket 处于
TIME_WAIT
状态,或父进程尚未关闭 listener。 父进程应在发送信号前先
listener.(*net.TCPListener).File()
,拿到 fd 后通过环境变量(如
LISTENER_FD=3
)或命令行参数传给子进程 子进程启动后第一件事是
os.NewFile()
+
net.FileListener()
,确认能
Accept()
再启动 HTTP server 父进程在子进程 ready 后再调
Shutdown()
,可通过子进程写临时文件或 socket 通信确认 HTTP/2 和 TLS 场景下的优雅重启注意事项 HTTP/2 连接复用程度高,单个连接可能承载多个请求,
Shutdown()
会等所有流完成才关闭连接;TLS 的
http.Server.TLSConfig
不影响优雅性,但若用
auto-tls
(如
certmagic
),证书热加载需额外同步,否则子进程可能因缺证书无法启动 HTTPS 监听。 如果用了反向代理(如 Nginx),它的 keepalive 设置会影响上游连接复用行为:Nginx 默认 keepalive 75 秒,若 Go 服务 shutdown 超时设太短,可能被 Nginx 主动断连。 HTTP/2 下不要在
Shutdown()
前关闭 TLS listener 单独,必须统一走
Server.Shutdown()
证书热更新需确保父子进程访问同一证书路径,或通过内存缓存共享(如用
certmagic.Cache
) 用
curl -v --http2
测试时注意观察
Connection #0 to host xxx left intact
是否出现,可验证连接是否复用 最易被忽略的是日志和 metrics 上下文延续性 —— 旧进程 shutdown 期间产生的日志、prometheus metrics 仍应正常上报,否则监控会出现断层。别让 graceful 逻辑本身变成新的单点故障源。

相关文章