recover必须在defer中调用才有效,仅在panic展开且defer执行时返回非nil值;goroutine间panic不传递,需各自封装recover;recover后状态不可回滚,应记录日志并退出而非继续业务。

recover 必须在 defer 中调用,否则恒返回 nil
直接写 recover() 没有用——它只在 panic 正在展开、且当前 goroutine 的 defer 链正在执行时才返回非 nil 值。普通函数体里调用,永远是 nil。
常见错误是把 recover 放在业务逻辑前或中间:
- ❌
if r := recover(); r != nil { ... }写在函数开头 - ❌ 在
panic之后才注册defer(此时 defer 根本不执行) - ✅ 正确顺序:先
defer func() { ... recover() ... }(),再执行可能 panic 的代码
goroutine 级别隔离:主协程 recover 不管子协程
HTTP handler 里加了 defer + recover,但请求没报错、连接却断了?大概率是子 goroutine panic 了,而你的 recover 完全无感。
Go 的 panic/recover 是 goroutine 局部机制,跨 goroutine 无效。必须每个可能出问题的 goroutine 自己包一层:
- ❌
go riskyFunc()—— 外层recover捕不到 - ✅
go func() { defer func() { recover() }(); riskyFunc() }() - ✅ 封装
goSafe(fn),内部自动注入defer+recover+debug.Stack()
recover 后不等于“安全”,副作用不会回滚
recover() 只是让 goroutine 软着陆,继续执行 defer 后面的代码;但它不关闭已打开的文件、不重置已修改的全局变量、不回滚已提交的数据库事务。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
所以 recover 后不要继续跑业务逻辑,尤其不能假设状态一致:
- ⚠️ 别在
recover后接着写入数据库或发 HTTP 请求 - ⚠️ 别对已 close 的 channel 再 send,或对 nil map 再赋值(这会触发新 panic)
- ✅ 常见做法:记录日志 +
debug.Stack()+ 主动退出该 goroutine(如 return 或 os.Exit(1))
性能与日志损耗:debug.Stack() 比 debug.PrintStack() 更可控
想记录堆栈又怕影响性能?debug.PrintStack() 直接输出到 stderr,没法统一格式、难过滤、易污染日志;而 debug.Stack() 返回 []byte,可按需处理。
低损耗的关键点:
- ✅ 生产环境避免无条件调用
debug.Stack(),先判断是否真需要(比如仅限 error 级别或特定 panic 类型) - ✅ 日志字段里存 stack 的哈希值(如
fmt.Sprintf("%x", sha256.Sum256(stack))),而非完整堆栈 - ✅ 使用结构化日志库(如
zerolog)的Stack()方法,它默认只在开发模式展开完整 stack
真正容易被忽略的是:recover 不是兜底银弹,它解决的是“不崩溃”,不是“不出错”。模块设计时,优先用 error 显式传递失败,而不是依赖 panic → recover 这条路径。后者只适合不可预知的运行时崩溃(如第三方库空指针、反射失败),不该用于业务逻辑分支控制。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










