recover只在当前goroutine有效,必须在同一goroutine中panic后、结束前执行defer+recover;无法捕获子goroutine panic、runtime.fatalerror及os.exit等终止;仅适用于http handler等有限场景。

recover 只在当前 goroutine 有效
recover 不是全局异常兜底机制,它只对**调用它的 goroutine 内部发生的 panic**起作用。如果 panic 发生在子 goroutine 中,而你在主 goroutine 调用 recover,完全捕获不到。
常见错误现象:panic: runtime error: index out of range 仍导致整个程序崩溃,即使外层写了 defer recover()。
- 必须在 panic 发生的同一 goroutine 中,且在
panic后、goroutine 结束前执行recover - 典型误用:在
go func() { ... }()外层 defer recover —— 这个 defer 属于主 goroutine,和子 goroutine 无关 - 正确做法:每个可能 panic 的 goroutine 内部自行加
defer func() { if r := recover(); r != nil { ... } }()
defer + recover 必须成对出现在同一函数作用域
不能把 defer 放在函数 A,把 recover 放在被调用的函数 B 里 —— recover 只能捕获当前 goroutine 中「最近一次未被处理的 panic」,且仅在 defer 函数执行期间有效。
使用场景:封装通用 panic 捕获逻辑时容易出错。
- 错误写法:
func wrap(f func()) { defer recover(); f() }——recover在wrap返回前就执行了,此时 panic 尚未发生 - 正确写法:
func wrap(f func()) { defer func() { if r := recover(); r != nil { log.Println("caught:", r) } }(); f() } - 注意:
recover()返回值为interface{},需类型断言才能获取具体 panic 值,比如r.(error)或fmt.Sprintf("%v", r)
recover 无法捕获 runtime.FatalError 类型崩溃
像 fatal error: all goroutines are asleep - deadlock、fatal error: stack overflow、或直接调用 os.Exit() 这类情况,recover 完全无效 —— 它们不属于 panic 流程,而是 runtime 强制终止。
性能 / 兼容性影响:别试图用 recover 替代合理错误处理或资源检查。
- 协程死锁、栈溢出、CGO 崩溃、内存耗尽等,都无法被
recover拦截 - 频繁 panic + recover 是反模式,会拖慢性能(panic 分配栈、展开堆栈开销大)
- 真正该用
recover的场景很窄:第三方库不可控 panic(如某些 unsafe 解析)、插件式代码沙箱、HTTP handler 级兜底
HTTP handler 中 recover 的典型安全写法
Web 服务中常用来防止一个请求 panic 导致整个 server 挂掉,但要注意 goroutine 边界和响应完整性。
func safeHandler(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
if r := recover(); r != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
log.Printf("Panic recovered: %v", r)
}
}()
h(w, r)
}
}
- 必须在 handler 函数内部 defer,确保与请求 goroutine 绑定
- 一旦
recover触发,w可能已部分写入,不能再调用w.Header().Set()或多次w.Write() - 若 handler 内启了新 goroutine(如异步日志、消息推送),需单独为其加 recover,否则它们 panic 仍会静默失败或污染日志
协程边界这事,不靠语言自动隔离,得靠人一层层显式加 defer。最容易漏的是嵌套 goroutine 和中间件链里的 recover 位置 —— 写完记得用故意 panic 测试下,别信“应该能捕获”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











