recover 必须在 defer 函数中调用才有效,且仅对当前 goroutine 中正在传播的 panic 生效;若未在 defer 中或 panic 已退出函数,则 recover 恒返回 nil。

recover 必须在 defer 函数里调用才有效
Go 的 recover 不是全局异常处理器,它只在当前 goroutine 的 panic 正在传播、且尚未退出函数时起作用。如果没包在 defer 里,或者写在普通代码流中,recover 永远返回 nil,什么也捞不到。
常见错误现象:recover() 返回 nil,日志没打,程序照样崩溃;或者误以为在 main 函数开头写个 defer recover() 就能兜住所有 panic——其实它只对 main 里直接触发的 panic 有效,子 goroutine 里的 panic 完全不影响它。
-
defer必须在 panic 可能发生的函数内注册,不能靠“父函数 defer 子函数 panic”来跨层捕获 - 多个
defer语句按后进先出顺序执行,recover应该放在最靠近 panic 触发点的defer中 - 如果函数内有多个 panic 路径(比如不同 if 分支),每个路径都要确保被同一层
defer覆盖
recover 后必须显式处理返回值,否则等于没做
recover() 返回的是 interface{} 类型的 panic 值,不是 error。不检查、不转换、不记录,就只是“让程序继续往下跑”,但你根本不知道刚才发生了什么。
使用场景:比如 HTTP handler 中防止一个请求 panic 导致整个服务挂掉,但如果不把 recover() 结果转成日志或监控指标,下次出问题连线索都没有。
立即学习“go语言免费学习笔记(深入)”;
- 永远用
if r := recover(); r != nil { ... }判断,别省略r != nil - 建议用
fmt.Sprintf("%v", r)或fmt.Sprint(r)转成字符串,避免类型断言失败导致二次 panic - 不要在
recover后继续用已损坏的状态(比如已 close 的 channel、已释放的资源),应尽快清理并返回
goroutine 内 panic 不会自动传播到主 goroutine
启动新 goroutine 时,它的 panic 完全独立。主线程里写的 defer recover() 对它毫无作用——这是 Go 并发模型的基本设计,不是 bug。
错误现象:后台任务 panic,控制台输出 panic stack,但主程序无感知、不记录、不告警,直到监控发现接口超时或连接数暴涨。
- 所有非主 goroutine 都要自己加
defer/recover,没有例外 - 推荐封装一个带 recover 的 goroutine 启动器,比如
go safeGo(func() { ... }) - 注意:recover 后无法“重新抛出” panic,所以不要指望用它做类似 try/catch re-throw 的逻辑
recover 不是错误处理替代品,滥用会导致失控状态
用 recover 捕获本该用 if err != nil 处理的业务错误(比如文件不存在、网络超时),会让代码难以调试、资源泄漏风险升高、测试覆盖变困难。
性能影响:panic/recover 是重量级操作,触发一次比普通错误分支慢 10–100 倍;频繁 panic 还会干扰 runtime 的栈追踪和 GC 行为。
- 只对真正意外的、不可预测的崩溃(如空指针解引用、切片越界、类型断言失败)用 recover
- 对可预期的错误(I/O、解析、校验失败等),坚持用 error 返回,别绕路
- 上线前用
go run -gcflags="-l" *.go检查是否无意中关闭了内联,导致某些 panic 被意外吞掉(极少见但存在)
真正难的不是写对 defer recover,而是判断哪里该让它发生、哪里必须让它发生、以及发生之后你敢不敢让它彻底结束那个 goroutine。










