recover必须在defer中调用才有效;它仅在panic发生前已注册的同一goroutine的defer函数内直接调用时生效,返回panic值,否则返回nil,且无法捕获运行时错误或跨goroutine panic。

recover必须在defer中调用才有效
Go没有传统意义上的异常机制,panic会立即终止当前goroutine的执行流,除非在同一线程(goroutine)中、且在panic发生前已注册defer语句,并在该defer里调用recover,否则无法捕获。常见错误是把recover()写在普通函数里,或放在defer之外——这时它永远返回nil。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
-
recover()必须紧挨着defer,且不能跨函数调用(比如封装成safeRecover()再调用,但没传入recover本身,就失效) - 典型写法是
defer func() { if r := recover(); r != nil { /* 处理 */ } }() - 注意:goroutine之间不共享
recover能力,子goroutine panic不会触发父goroutine的defer里的recover
HTTP中间件中recover的标准封装模式
在HTTP服务中,最常需要recover的是handler执行过程。目标是不让一次请求panic导致整个server崩溃,同时记录错误、返回500响应。关键点在于:恢复逻辑必须在handler执行的同一goroutine内,且要在实际业务逻辑开始前就注册defer。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 写一个通用中间件函数,如
func RecoverMiddleware(next http.Handler) http.Handler,内部包装http.HandlerFunc并插入defer块 - 不要在
next.ServeHTTP之后调用recover——那时已经晚了;必须在调用前就注册defer,确保它在panic发生时能执行 - 示例核心片段:
func (mw *recoveryMW) ServeHTTP(w http.ResponseWriter, r *http.Request) { defer func() { if err := recover(); err != nil { log.Printf("panic recovered: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }() mw.next.ServeHTTP(w, r) } - 注意
http.Error可能失败(比如header已写),建议先检查w.Header().Get("Content-Type")是否为空,或使用http.NewResponseWriter包装增强容错
recover捕获不到所有“崩溃”,别误以为它等价try-catch
recover只对panic有效,对运行时错误(如空指针解引用、除零、栈溢出)或OS级信号(如SIGSEGV)无效。Go运行时遇到这些会直接终止程序,defer+recover完全不起作用。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 不要指望
recover兜底内存越界或协程泄漏——这类问题需靠静态分析(go vet)、race detector(go run -race)和pprof定位 - 某些panic本身不可恢复,例如
runtime.Goexit()触发的panic,recover返回nil - 如果业务中大量依赖
panic做流程控制(比如解析失败就panic),要警惕性能损耗:每次panic都会构建完整栈帧,比返回error慢1~2个数量级
日志与错误透传:recover后别丢掉原始上下文
单纯recover()拿到interface{}值,往往只是字符串或简单结构体,缺乏堆栈、请求ID、时间戳等关键诊断信息。线上出问题时,光看"index out of range"根本没法定位。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
debug.PrintStack()或runtime.Stack(buf, false)手动抓取当前goroutine栈,和recover()结果一起记日志 - 如果中间件已接入trace系统(如OpenTelemetry),应在
recover分支里显式span.RecordError(err)并标记span.SetStatus(…) - 避免在
recover块里启动新goroutine处理日志(如go log.Printf(…))——万一panic反复触发,可能堆积大量goroutine - 考虑将
panic内容序列化进HTTP响应头(如X-App-Panic),仅限调试环境,生产环境务必关闭
真正难的不是写对recover语法,而是判断哪些panic该捕获、哪些该让程序立刻退出,以及捕获后如何不掩盖真实问题。线上服务里,过度recover反而会让内存泄漏或死锁更难被发现。


















