recover必须写在defer函数中才有效,若在普通分支调用则永远返回nil;需在panic可能发生前注册defer,且每个goroutine须独立处理panic,recover后需手动清理资源并谨慎续行。

recover 必须写在 defer 函数里,否则完全无效
这是最常踩的坑:把 recover() 写在普通逻辑分支中,比如 if err != nil { recover() },它永远返回 nil。因为 recover() 只有在 panic 正在发生、且当前 goroutine 还没退出时,**且**在 defer 延迟执行的函数体内调用,才可能拿到 panic 值。
- ✅ 正确姿势:用
defer func() { if r := recover(); r != nil { /* 处理 */ } }()包裹 - ❌ 错误姿势:在 panic 发生后、defer 执行前的任意位置调用
recover() - ⚠️ 注意:
defer语句必须出现在 panic 可能发生的位置之前——推荐统一放在函数开头,避免条件判断漏注册
goroutine 的 panic 无法被外层 recover 捕获
主 goroutine 里的 defer + recover 对子 goroutine 完全不起作用。子 goroutine panic 后会静默退出,不报错、不传播、不阻塞,只留下资源泄漏和逻辑断点。
- 每个可能出错的 goroutine 都要自己加
defer func() { recover() }() - 别指望在
main()或 handler 外层包一层 recover 就能兜住所有并发错误 - 封装成工具函数更可靠,例如:
safeGo(func() { ... }),内部自动注入 recover 逻辑
recover 后程序继续执行,但状态已不可信
recover() 能让 goroutine 从 panic 中“活过来”,但它不会回滚变量、修复内存、重置锁或关闭文件。你只是抢在程序崩溃前拿到了控制权,接下来得自己善后。
- 不要在事务中间(如 DB commit 前)依赖 recover 来“恢复业务”
- 优先做清理:关闭打开的
file、conn、cancel()context - 记录日志时建议加上
runtime/debug.Stack(),否则只有 panic 值,没有堆栈线索 - 返回 error 或上报监控比“假装没事继续跑”更安全
HTTP 中间件是 recover 最典型的落地场景
Web 服务里,一个 handler panic 会导致整个请求协程崩溃,但默认不会终止进程——可利用这点,在入口层统一兜底,防止单个错误拖垮服务。
立即学习“go语言免费学习笔记(深入)”;
- 中间件模式成熟稳定:
recoverMiddleware(http.Handler)是标准做法 - 务必在
next.ServeHTTP()之前注册 defer,否则来不及捕获 - 返回
http.Error(w, ..., 500)时,记得设好 Content-Type 和 status code,避免前端卡死 - 生产环境建议加采样率控制,避免 panic 高频时日志刷爆磁盘
真正难的不是写对 recover(),而是判断该不该 recover:空指针、切片越界、向已关闭 channel 发送数据……这些系统级 panic 通常意味着代码缺陷,强行 recover 只会让问题更隐蔽。业务层的“非法参数”“配置缺失”之类,早该用 error 返回,而不是扔进 panic 里等 recover 去捞。










