recover 必须放在每个业务 goroutine 入口处显式使用,因它仅对当前 goroutine 生效;错误地置于 handler 中无法捕获子 goroutine panic,导致资源泄漏;正确做法是在 processOne 等实际处理函数开头加 defer+recover,并配以显式资源清理与降级逻辑。

recover 不能自动兜底批量请求里的 panic,必须在每个请求处理的 goroutine 入口显式加 defer+recover,否则 panic 会直接杀死该 goroutine 并泄漏资源。
为什么 HTTP handler 里开 goroutine 处理请求时 recover 失效
主 goroutine 的 defer 函数捕获不到子 goroutine 的 panic —— recover 只对当前 goroutine 生效。常见错误是只在 handler 函数里写一个 defer func() { recover() }(),然后在内部启动多个 goroutine 去处理每条请求,结果这些 goroutine 一 panic 就静默退出。
- goroutine panic 后不会传播到父 goroutine,主流程完全无感知
- 未关闭的
http.ResponseWriter、未释放的数据库连接、未 unlock 的互斥锁都会残留 - 反复触发会导致文件描述符耗尽、连接池打满、内存缓慢增长
批量请求场景下 recover 的正确安放位置
必须把 recover 放在真正执行业务逻辑的 goroutine 内部,也就是“每个请求的最外层函数”里。不是放在 handler 函数里,而是放在你 go processOne(req) 调用的那个 processOne 函数开头。
- 错误写法:
func handler(w http.ResponseWriter, r *http.Request) { go processOne(r); defer recover() }—— 这个recover永远不生效 - 正确写法:
func processOne(r *http.Request) { defer func() { if r := recover(); r != nil { log.Printf("panic in req %s: %v", r.URL.Path, r) } }(); /* 实际逻辑 */ } - 如果用了
sync.WaitGroup等待批量 goroutine,记得在recover分支里也调用wg.Done(),否则可能卡死
recover 捕获后怎么安全地记录和降级
recover 返回的是 interface{},不能直接断言为 error,尤其 runtime 抛出的 panic(如 index out of range)底层类型不可导出,err.(error) 会再次 panic。
立即学习“go语言免费学习笔记(深入)”;
- 最稳妥的日志方式是
fmt.Sprintf("%v", r),能输出可读字符串且不会崩溃 - 不要在 recover 后继续使用可能已损坏的对象:比如 panic 是因
user.Name为空指针导致的,recover 后再访问user.Name仍会 crash - 降级逻辑必须显式编写:比如 fallback 到默认值、跳过当前项、写入失败队列,不能指望 recover 自动回滚或重试
- 避免在 recover 分支里调用可能 panic 的函数(如
json.Marshal),否则会丢失原始 panic 信息
容易被忽略的资源清理细节
recover 只阻止崩溃,不自动清理资源。如果你在 panic 前打开了文件、获取了锁、写了 channel,这些动作不会被撤销。
- 所有资源释放逻辑必须显式写在 defer 里,且要放在 recover 的 defer 之前或同一层(因为 defer 按 LIFO 执行)
- 推荐结构:
defer file.Close()→defer mu.Unlock()→defer func() { recover() }(),确保无论是否 panic,资源都先释放 - HTTP handler 中尤其注意:
defer r.Body.Close()和defer json.NewDecoder(r.Body).Decode(...)之后的 panic,body 可能已部分读取,无法重复读取
批量请求的 panic 处理不是加一层 recover 就完事,关键在于 goroutine 边界是否清晰、资源释放是否与 panic 路径解耦、以及 recover 后的行为是否可控。线上服务里,漏掉一个 goroutine 的 recover,就可能演变成缓慢的资源泄漏,而不是立刻报错——这种问题最难排查。


















