beego.bconfig.listen.graceful = true 仅启用http层优雅停机,不自动管理数据库等资源;需手动注册shutdown hook并在handler中使用context控制i/o。

beego.BConfig.Listen.Graceful = true 是开关,不是全部
这个配置项只控制 HTTP 层是否启用优雅停机逻辑,它本身不接管数据库、Redis、gRPC 客户端或自定义 goroutine 的生命周期。开启后,beego 会监听 SIGINT 和 SIGHUP,但仅限于:收到 SIGHUP 时 fork 新进程并让老进程等待现有连接关闭;收到 SIGINT(如 Ctrl+C)时尝试 graceful shutdown。它不会自动调用你写的 db.Close() 或 redisPool.Close()。
必须手动注册 shutdown hook,且时机很关键
beego 没有提供类似 Spring 的 @PreDestroy 或 Gin 的 srv.RegisterOnShutdown() 接口,所以你要在应用启动后、服务真正开始接收请求前,显式注册自己的清理函数。常见错误是把关闭逻辑写在 main() 函数末尾——那根本不会执行,因为 beego.Run() 是阻塞的。
- 正确做法:在
func main()中调用beego.BeeApp.Shutdown()前,先用runtime.SetFinalizer或更稳妥的os.Signal监听 + 单独 goroutine 处理 - 推荐路径:在
init()或main()开头启动一个 goroutine,signal.Notify(c, syscall.SIGTERM, syscall.SIGINT),收到信号后调用自定义的cleanup() - 注意:不要在 handler 里直接调用
os.Exit(0),这会跳过所有 cleanup
HTTP handler 必须响应 context.Context 取消
即使你启用了 Graceful = true 并监听了信号,如果 handler 里写了 db.Query("SELECT ...") 而不是 db.QueryContext(r.Context(), "SELECT ..."),或者用了没带 ctx 的 HTTP client 请求下游服务,那么 srv.Shutdown() 触发后,这些阻塞操作仍会卡住,导致超时退出(默认 15 秒),最终被强制 kill。
- 所有外部 I/O 操作都应使用带
context.Context的变体:http.Client.Do(req.WithContext(ctx))、redis.Client.Get(ctx, key)、gorm.DB.WithContext(ctx).First(&u) - 长轮询或 SSE handler 要在每次循环内检查
ctx.Done(),并主动 return - 避免在 handler 中启动无 context 约束的 goroutine,比如
go sendEmail()—— 应该写成go func() { select { case
容器环境下的 SIGTERM 超时要对齐
Kubernetes 默认给 pod 发送 SIGTERM 后等待 30 秒(terminationGracePeriodSeconds),而 beego 的 graceful shutdown 默认超时是 15 秒(硬编码在源码中),两者不一致会导致容器被强制 SIGKILL,优雅逻辑白做。
- 不能靠改 beego 源码,应在外层封装:用
http.Server替代 beego 内置 server,自己控制srv.Shutdown()的 timeout - 实际做法:禁用
beego.BConfig.Listen.Graceful,改用标准http.Server启动,handler 仍用 beego 的Engine(它实现了http.Handler) - 示例关键片段:
srv := &http.Server{Addr: ":8080", Handler: beego.BeeApp.Handlers},然后在 signal handler 里调srv.Shutdown(context.WithTimeout(context.Background(), 25*time.Second))
context.Context,以及容器平台的超时设置是否与你的 shutdown 逻辑匹配。漏掉任意一环,都会让 Graceful = true 形同虚设。











