recover必须在defer中调用才有效,且仅捕获同goroutine内未退出函数前的panic;recover后函数直接返回,不继续执行panic后语句;panic仅用于不可恢复的编程错误,非业务错误。

recover 必须在 defer 中调用才有效
Go 的 recover 不是全局异常处理器,它只在当前 goroutine 的 panic 正在传播、且尚未退出函数时生效。如果没用 defer 包裹,recover 会立即返回 nil,什么也抓不到。
常见错误写法:
func bad() {
recover() // 永远返回 nil
panic("boom")
}
正确姿势是:
-
defer必须在可能 panic 的代码之前注册 -
recover()要放在defer的匿名函数里,不能提前调用 - 一个
defer只能捕获本函数内发生的 panic;跨函数 panic 需在调用方处理
recover 后程序不会自动“继续执行”
很多人误以为 recover 类似 try/catch 中的 catch —— 恢复后还能接着跑下面的语句。实际上,recover 只是让 panic 停止传播,控制权回到 defer 所在函数的末尾,之后该函数直接返回,**不会跳回 panic 发生点继续执行**。
示例:
立即学习“go语言免费学习笔记(深入)”;
func demo() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}()
fmt.Println("before panic")
panic("oh no")
fmt.Println("this line never runs") // 确实不会运行
}
- panic 发生后,函数栈开始展开,
defer被触发,recover()成功拿到值 - 但函数仍会立即返回,后续语句被跳过
- 想“重试”或“兜底”,得靠业务逻辑自己判断和分支,不是 recover 自带能力
不要用 panic 替代 error 返回
Go 社区共识:panic 仅用于**真正不可恢复的编程错误**(如索引越界、nil 指针解引用、断言失败),而不是业务错误(如文件不存在、网络超时、参数校验失败)。
滥用 panic 的后果:
- 调用方无法静态检查错误路径,破坏 Go 的显式错误处理哲学
- 中间件或框架(如 HTTP handler)若没包一层 recover,整个服务会崩溃
- goroutine 泄漏风险:未 recover 的 panic 会让 goroutine 终止,但若它持有资源(如 channel send、锁),可能引发死锁或状态不一致
典型反例:
// ❌ 错误:把可预期的错误转成 panic
func ReadConfig(path string) *Config {
data, err := os.ReadFile(path)
if err != nil {
panic(err) // 不该由这里决定是否 crash
}
// ...
}
HTTP 服务中 recover 的典型封装位置
Web 服务最常需要全局 panic 捕获,但不是每个 handler 都手动加 defer。推荐在中间件或路由层统一处理:
- 在
http.HandlerFunc内部做defer recover(),是最小侵入方式 - 避免在
main()或http.ListenAndServe()外层 recover —— 它不生效,因为 panic 发生在 handler goroutine 里 - recover 后建议记录 stack trace(用
debug.PrintStack()或runtime/debug.Stack()),否则日志里只有 panic message,难定位
精简中间件示例:
func recoverMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if r := recover(); r != nil {
log.Printf("PANIC in %s %s: %+v", r.Method, r.URL.Path, r)
debug.PrintStack()
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
真正棘手的是 recover 后的状态一致性 —— 比如 panic 发生在数据库事务中间,recover 了但事务没 rollback,这个得靠业务自己兜底,语言不负责。










