recover仅在同goroutine的defer中有效,须在panic前注册;推荐在HTTP handler、goroutine入口等可信边界统一recover,而非每层嵌套添加。

在 Go 中,recover 只能在 defer 函数中生效,且仅对**当前 goroutine** 中由 panic 触发的异常起作用。它不能“跨函数”自动捕获上层调用链中的 panic —— 换句话说,recover 必须放在 panic 发生的同一 goroutine 的 defer 中,且必须在 panic 之后、程序终止之前执行。所谓“多级函数调用”的 panic 保护,并不是靠某一层 recover 去拦截其他层的 panic,而是要确保 panic 发生时,其**传播路径上至少有一处 defer+recover 组合能及时介入**。
关键原则:recover 必须与 panic 在同一 goroutine,且 defer 要在 panic 前注册
Go 的 panic/recover 是 goroutine 局部机制。即使 A 调用 B,B 调用 C,C panic 了,只要 A 或 B 中有 defer+recover(且该 defer 已注册),就能捕获——但前提是这个 defer 是在 panic 触发前就已安排好(例如函数入口处写 defer recover() 是无效的,因为还没执行到那里)。
常见错误写法:
red">func A() {
B() // 如果 B panic,这里没 defer,无法 recover
}
func B() {
C()
}
func C() {
panic("boom")
}上面代码中,没有任何 recover,panic 会一路向上直到进程崩溃。
立即学习“go语言免费学习笔记(深入)”;
推荐做法:在可能触发 panic 的入口或关键边界处统一 recover
最实用的方式不是每层都加 recover,而是在**可信边界**(如 HTTP handler、goroutine 入口、RPC 方法、定时任务主循环)做一次集中 recover。这样既简洁,又避免业务函数被异常处理逻辑污染。
- HTTP handler 示例:
func safeHandler(f http.HandlerFunc) http.HandlerFunc {
return func(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)
}
}()
f(w, r)
}
}
http.HandleFunc("/api", safeHandler(myBusinessLogic))
- goroutine 启动时兜底:
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("goroutine panic: %v", r)
}
}()
doWork() // 可能深层调用 panic
}()如果真需要在中间层主动捕获(比如封装可恢复的工具函数)
可以设计带 recover 的包装函数,显式返回 error,把 panic 转为可控错误:
func safeCall(fn func()) (err error) {
defer func() {
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = fmt.Errorf("panic: %s", x)
case error:
err = fmt.Errorf("panic: %w", x)
default:
err = fmt.Errorf("panic: %v", x)
}
}
}()
fn()
return nil
}
// 使用:
if err := safeCall(func() { mayPanic() }); err != nil {
// 处理 panic 转换来的 error
}
这种方式让调用方决定是否处理“潜在 panic”,适合工具库或测试辅助。
注意事项:recover 不是 try-catch,别滥用
-
recover()只在 defer 中且 panic 正在发生时才返回非 nil 值;其他时候返回 nil - 不能 recover 别的 goroutine 的 panic
- 不要用 recover 替代正常错误处理(比如空指针、越界应提前检查,而不是等 panic 再 recover)
- recover 后程序会继续执行 defer 之后的语句,但栈已展开,原 panic 点上下文丢失










