recover不是异常处理框架,而是Go运行时内建函数,必须配合defer在当前goroutine中使用;HTTP中间件中的recover看似全局实则每个请求goroutine独立执行;子goroutine panic无法被父goroutine recover捕获;recover必须置于defer函数内且注册在panic前;recover仅停止panic传播,不回滚副作用,需手动清理资源;所有后台goroutine都须自行defer+recover,推荐封装safeGo。

使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
recover 不是异常处理框架,它只是 Go 运行时提供的一个内建函数,必须配合 defer 在当前 goroutine 内使用 —— 没有“Recovery 框架”这种东西,所有所谓“统一捕获”都只是封装了这个基础机制。
为什么 HTTP 中间件里的 recover 看起来像全局捕获?
它不是全局的,只是每个请求都在独立 goroutine 中执行,而中间件的 defer func() { recover() }() 被插在每个请求 handler 入口处。所以:
- 每个请求 goroutine 都有自己的
recover,互不干扰 - 它对
go func() { panic("x") }()完全无效 —— 子 goroutine 的 panic 不会传播过来 - 如果 handler 里启动了后台 goroutine(比如日志异步写入),那个 goroutine 必须自己包
defer+recover
recover 必须写在 defer 函数里,且顺序不能错
常见错误是把 recover() 写在普通代码块里,或者放在 panic 之后注册 defer:
- ❌
if r := recover(); r != nil { ... }直接写在函数体中 → 恒返回nil - ❌
panic("boom"); defer func() { recover() }()→defer根本不会执行 - ✅ 正确姿势:函数开头就注册
defer func() { if r := recover(); r != nil { ... } }() - ✅ 如果函数内有多个 panic 可能点(比如不同分支调用第三方库),这个
defer必须覆盖全部路径
recover 后程序能继续跑,但状态可能已损坏
recover 只是停止 panic 传播,不回滚任何副作用:
- 已打开的文件句柄、数据库连接、channel 发送操作都不会自动撤销
- 如果 panic 发生在事务中间,
recover后直接 return 而不 rollback,会导致数据不一致 - 不要在
recover后继续用已 close 的 channel 或已释放的资源,容易二次 panic - 建议模式:记录堆栈(
debug.Stack())、清理关键资源、返回明确错误,而不是“假装没事继续执行”
goroutine 泄漏比 panic 更隐蔽,也更难捕获
你加了中间件 recover,HTTP 请求不崩了,但后台 goroutine 每次 panic 都没日志、没告警、没回收:
- 所有
go func() { ... }()都必须自己带defer+recover,没有例外 - 推荐封装一个安全启动函数:
safeGo(func() { ... }),内部自动包一层defer recover - 别依赖 “主线程 defer 就能兜住一切”,这是 Go 并发模型的基本事实,不是 bug
- 如果用了
sync.WaitGroup或context控制生命周期,panic 后记得显式 Done 或 cancel,否则 goroutine 永远卡住
recover,而是判断哪些 panic 值得 recover —— 空指针解引用和除零错误,恢复后继续跑大概率引发更严重问题。

















