fiber 框架本身不提供开箱即用的优雅关闭能力,必须手动接管 *http.server、监听 sigterm/sigint、调用带超时 context.withtimeout 的 srv.shutdown(),并依次关闭 db、redis 等资源;app.listen() 是阻塞式封装,不暴露 server 实例,无法触发 shutdown,直接导致连接 rst 和事务中断。

Fiber 框架本身不提供开箱即用的优雅关闭能力,必须手动接管 *http.Server、监听系统信号、调用带超时的 Shutdown(),并协调 DB、Redis、后台 goroutine 等资源释放 —— 否则进程直接退出,连接 RST、事务中断、SSE 重连丢失 last-event-id 都会真实发生。
为什么 app.Listen() 不能用于生产级关闭
它内部调用 http.Server.ListenAndServe(),是阻塞式启动,不返回 *http.Server 实例,也不接受 context.Context。一旦收到 SIGTERM,Go 运行时立即终止主 goroutine,所有活跃 HTTP 连接被强制断开,connection reset by peer 错误频发。
- 调用
app.Listen(":8080")后,你拿不到srv引用,后续无法调用srv.Shutdown() - 即使你在别处另起一个
*http.Server,app.Handler()是可复用的,但app.Listen()已独占端口并阻塞,造成冲突 - K8s 的
terminationGracePeriodSeconds到期后发SIGKILL,此时若Shutdown()还没完成,进程被强杀
怎么从 fiber.App “抠出” *http.Server 并启动
核心是放弃 app.Listen(),改用显式构造 *http.Server + srv.Serve(ln) 启动方式,同时确保 srv 变量在 shutdown 阶段仍可访问。
- 用
net.Listen("tcp", ":8080")创建 listener,避免srv.ListenAndServe()重复绑定 -
srv := &http.Server{Handler: app.Handler(), Addr: ":8080"}—— 注意不是app本身,而是app.Handler() - 启动必须放在 goroutine 中:
go srv.Serve(ln),否则主 goroutine 阻塞,收不到信号 - 保留对
srv的引用(比如定义为包级变量或传入信号处理函数),后续srv.Shutdown()全靠它
srv.Shutdown() 必须带超时 context,且 handler 要响应取消
Shutdown() 只做两件事:关闭 listener 拒绝新连接 + 等待已有 handler 返回。它不设默认超时,也不中断卡住的 DB 查询或 HTTP client 调用 —— 这部分得你自己管。
- 错误写法:
srv.Shutdown(context.Background())→ 可能永久 hang 住 - 正确写法:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second),然后srv.Shutdown(ctx) -
Shutdown()返回后,必须检查错误:if err != nil && err != http.ErrServerClosed才算真异常 - 不要在
Shutdown()后再调用srv.Close(),会 panic
如何同步等待 DB 事务和后台任务完成
http.Server.Shutdown() 不感知数据库事务、长轮询 goroutine、消息确认等逻辑,必须显式协调。
- 对支持 context 的驱动(如
pgx/v5),把 shutdownctx传给db.BeginTx(ctx, ...)和tx.Commit(ctx),超时自动 rollback - 用
sync.WaitGroup记录活跃事务 goroutine,每个结束时调用wg.Done();shutdown 时wg.Wait()阻塞等待 - 若用 GORM,
db.Close()不等待未提交事务,需先手动tx.Commit()或tx.Rollback() - 避免在 shutdown 流程中新开 goroutine,否则
WaitGroup无法追踪
最容易被忽略的是:handler 内部是否真正响应了 context 取消。比如一个 time.Sleep(60 * time.Second) 不受 ctx.Done() 影响,就会拖垮整个 Shutdown() 流程。所有阻塞操作(DB 查询、HTTP 调用、channel receive)都应带 context 或 timeout。这不是 Fiber 的限制,而是 Go HTTP server 的通用契约。











