recover必须在defer中调用才有效,且仅对当前goroutine的panic生效;直接在函数体中调用无效,HTTP handler中应首行使用defer+recover捕获并安全响应。

recover必须在defer中调用才有效
Go 的 recover 不是全局异常捕获机制,它只在当前 goroutine 的 panic 正在被传播、且尚未退出函数时起作用。如果不在 defer 里调用,recover 永远返回 nil —— 因为函数已经 return 或 panic 已经终止了 goroutine。
常见错误写法:if err := recover(); err != nil { ... } 直接放在函数体开头或中间,完全无效。
-
recover()必须包裹在defer函数中,且该defer必须在 panic 发生前已注册(即在 panic 前执行过 defer 语句) - 不能在单独启动的 goroutine 里靠外层 defer 捕获其 panic;每个 goroutine 需要自己配
defer + recover - HTTP handler 中最稳妥的位置是:在 handler 函数第一行就写
defer func() { if r := recover(); r != nil { ... } }()
HTTP handler 中 recover 的典型结构
标准 net/http handler 是一个无返回值函数,panic 会直接导致整个 HTTP 连接中断并输出默认 500 页面(或更糟:连接被 reset)。需要手动拦截 panic 并转为可控响应。
下面是最简可用结构:
立即学习“go语言免费学习笔记(深入)”;
func myHandler(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)
}
}()
// 正常业务逻辑,可能触发 panic
riskyOperation()
}
- 注意
http.Error()要在recover()之后调用,否则可能因 panic 已破坏 response 状态而失败 - 不要在 recover 块里尝试继续写入
w多次,response 可能已被部分写出,应只做一次错误响应 - 如果用了自定义 ResponseWriter 包装器(如记录 body 大小),需确保它支持 panic 后安全调用
WriteHeader
recover 捕获不到的“异常”类型
recover 只对 panic 生效,对其他常见错误完全无感。很多开发者误以为它能替代错误处理,结果线上漏掉大量可预知失败。
- 返回
error值的函数(如json.Unmarshal()、db.QueryRow().Scan())出错时不会 panic,必须显式检查err != nil - 空指针解引用(
nil pointer dereference)确实会 panic,但这是运行时错误,不是业务错误;不应依赖 panic 来暴露逻辑缺陷 - goroutine 泄漏、死锁、channel 关闭后读写等也不会触发 panic,
recover对它们毫无作用 - Go 1.22+ 的
os.Exit()和失控的runtime.Goexit()也无法被 recover 拦截
recover 后的日志与响应设计要点
单纯 recover + 打印日志还不够。生产环境需要区分 panic 类型、保留上下文、避免敏感信息泄露。
- 用
debug.PrintStack()或runtime/debug.Stack()获取完整堆栈,但别直接返回给客户端(含路径/变量名) - 对已知 panic 场景(如第三方库明确文档写了 panic 条件),可做类型断言判断:
if r, ok := r.(string); ok && strings.Contains(r, "timeout") { ... } - 不要在 recover 块里调用可能再次 panic 的函数(比如未判空的 map 写入、未初始化的 logger)
- 若使用 zap/logrus 等结构化日志,建议附加
request_id、method、path,方便追踪
recover 不是兜底银弹。它只负责把崩溃拉回控制流,真正的健壮性来自防御性编码、显式错误检查和合理超时控制。最容易被忽略的是:recover 后的程序状态未必一致——比如 struct 字段可能只初始化了一半,后续逻辑仍可能出错。


















