go服务真正不丢请求退出和重启,必须三者协同:http.server.shutdown()配合信号监听(sigint/sigterm)、文件描述符(fd)继承复用socket、所有handler响应ctx.done();缺一不可,否则断连、报address already in use或泄漏goroutine。

Go服务要真正不丢请求地退出和重启,只靠http.Server.Shutdown()远远不够——它不监听信号、不传递socket fd、对长连接无感知。必须手动补全信号注册、fd继承、上下文传播三块拼图,否则Ctrl+C一按就断连,SIGUSR2一发就报bind: address already in use。
为什么server.Close()不是优雅关闭
server.Close()会立刻关闭监听器并强制终止所有已接受但未完成的连接。它不等handler返回、不等response写完、甚至不给time.Sleep(1)留机会。常见现象包括:
- 客户端收到
connection reset by peer或空响应 - 大文件上传卡在87%后失败
- 长轮询接口突然断连,前端反复重试
-
ListenAndServe()立即返回非http.ErrServerClosed错误,日志里满屏panic
这不是“关得快”,是“关得糙”。Go官方明确标注:仅用于测试或极端清理场景,生产环境禁用。
如何正确使用http.Server.Shutdown()
Shutdown()本身不触发退出,只是“开始等待”:停止accept新连接,但会一直等已有请求自然结束。若没监听信号,程序就卡住不动。关键实操点:
- 必须用
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)注册信号通道;别只监听SIGHUP——systemd默认发SIGTERM - 超时
context.Context必须干净:ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second);别defer cancel(),否则Shutdown()没开始就取消了 -
Shutdown()不会主动cancel handler里的req.Context(),所以每个handler内部必须检查ctx.Done(),比如用db.QueryContext(ctx, sql)而非db.Query() - 忽略
http.ErrServerClosed:这是ListenAndServe()正常退出的标志,不是错误
平滑重启时socket fd怎么安全传递
Linux下不能靠“新进程自己listen”,必须把老listener的fd传给新进程。标准库不自动支持,得手动做:
- 父进程收到
SIGUSR2后,调ln.(*net.TCPListener).File()获取*os.File,记下file.Fd()(如3) - 启动子进程时,在
os.StartProcess的sys.ProcAttr.Files中填入[]uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd(), 3} - 子进程启动后第一件事:读
os.Getenv("LISTEN_FD"),用os.NewFile(3, "")恢复*os.File,再用net.FileListener(file)转成net.Listener - 验证是否成功:
lsof -i :8080应同时看到两个PID;若只看到一个,说明fd传递失败或子进程没调net.FileListener
长连接(WebSocket/SSE)为什么还是断了
Shutdown()只管HTTP连接生命周期,对已升级的长连接完全无感知。WebSocket升级后脱离http.Server管理,底层TCP还在跑;SSE响应头设了text/event-stream,但handler里没监听req.Context().Done()就会卡住。必须手动管理:
- 升级后的conn存进
sync.Map,shutdown信号来了能遍历close - WebSocket读写前加
select { case ,或用<code>conn.SetReadDeadline()/SetWriteDeadline()配合 - 超时时间别设太短(比如5s),上传大文件或流式响应直接被强制中断
- 中间件里启动的goroutine(如日志异步刷盘)必须监听同一
ctx,否则会拖慢shutdown
最易被忽略的是:handler里所有可能阻塞的操作(数据库查询、HTTP调用、定时等待)都得主动响应ctx.Done(),否则Shutdown()会一直等,直到超时强杀goroutine——而这时连接早已断开,用户根本收不到响应。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











