recover 只在 defer 函数内调用才有效,且仅能捕获当前 goroutine 的 panic;它不能恢复执行到 panic 点,仅用于兜底日志或降级,不可替代 error 处理。

panic 会直接终止 goroutine,recover 只在 defer 中有效
Go 的 panic 不是异常,它会立即停止当前 goroutine 的执行,除非被 recover 拦截。但 recover 只有在 defer 函数里调用才起作用——放在普通函数体里、或者 defer 之外,它永远返回 nil。
常见错误现象:recover() 返回 nil,程序还是崩溃;或 panic 后日志没打出来,因为 defer 没覆盖到 panic 发生点。
-
recover()必须写在defer函数内部,且该defer必须在 panic 触发前已注册(即 panic 前就执行了 defer 语句) - 不要在多层嵌套函数中“传递” recover —— 它只对当前 goroutine 的最近一次 panic 生效
- 如果想捕获子 goroutine 的 panic,必须在那个 goroutine 内部加 defer + recover;父 goroutine 无法代劳
recover 后程序继续执行,但栈已展开,状态可能不一致
recover 成功后,goroutine 不会回到 panic 发生点,而是从 defer 函数返回后继续往下走。这意味着:panic 之前的中间状态(比如部分更新的 struct 字段、未关闭的文件句柄、未回滚的内存缓存)很可能已经损坏。
使用场景:适合做“兜底日志记录”或“优雅降级”,不适合用来“修复错误后重试”。比如 HTTP handler 中 recover 后返回 500,而不是试图继续处理请求。
立即学习“go语言免费学习笔记(深入)”;
- recover 后别假设变量还处于预期值;尤其注意指针、map、slice 等引用类型是否已被部分修改
- 避免在 recover 分支里调用可能再次 panic 的逻辑(如访问可能为 nil 的接口、解包未校验的 JSON)
- 如果业务要求强一致性(如转账),panic 应该是不可恢复的信号,此时 recover 只用于记录和通知,不用于继续流程
不要用 panic/recover 替代错误返回
Go 的哲学是“error 是值”,panic 应仅用于真正意外、不可恢复的场景(如 nil 指针解引用、数组越界、断言失败)。把业务错误(如用户输入非法、DB 查询无结果)用 panic 处理,会导致调用方完全无法感知和响应。
性能影响:panic+recover 比返回 error 慢 1–2 个数量级,因为涉及栈展开和 runtime 干预;频繁触发会显著拖慢吞吐。
- HTTP handler、CLI 命令、数据库操作等常规路径,一律用
if err != nil判断,而不是 defer+recover 包裹整个函数 - 第三方库若内部 panic(如某些 JSON 解析器对非法 UTF-8 的处理),才需要你在调用侧加 recover —— 这属于防御性兜底,不是常态设计
- 测试中故意触发 panic 验证 recover 行为时,记得用
defer func() { recover() }(),否则 test 会直接失败
全局 panic 捕获要谨慎:runtime.SetPanicHandler 不等于 try/catch
Go 1.23+ 引入了 runtime.SetPanicHandler,但它只在所有 defer 都执行完、程序即将退出前被调用,**无法阻止崩溃**,也不能恢复执行。它只是最后一个“遗言”机会。
兼容性影响:低于 Go 1.23 的环境完全不识别这个函数;且它不接收 panic 值,只拿到一个 panicInfo 结构,字段有限。
- 适合场景:记录最后的 panic 堆栈到远端监控、触发 crash report、清理共享资源(如临时目录)
- 不能依赖它来“救活”服务;HTTP server 崩溃后不会自动重启,仍需外部进程管理(如 systemd、supervisord)
- 若同时用了
recover和SetPanicHandler,后者只会收到未被 recover 拦截的 panic











