recover不能保证程序不崩溃,只能防止goroutine静默退出或服务整体挂掉;必须在HTTP handler、子goroutine、main函数中显式defer+recover,且recover后仅限日志与清理,不可修复或重试。

不能靠 recover 保证“程序不崩溃”,只能防止 panic 导致 goroutine 静默退出或服务整体挂掉——关键在“在哪加、怎么加、加了之后做什么”。
HTTP handler 必须手动 defer+recover
标准库 http.ServeHTTP 内部会 recover,但只记录日志、不写响应,结果是客户端收不到任何内容(空白响应或超时),而服务进程还在跑。这不是“程序没崩溃”,而是“请求已死亡”。
常见错误现象:curl -v 显示 Content-Length: 0 或连接重置,panic: runtime error: invalid memory address 日志却没出现在业务日志里。
- 必须在每个 handler 函数开头或中间件里显式写
defer func() { if r := recover(); r != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }() - 别依赖“全局中间件”——如果 panic 发生在中间件之后(比如业务函数内部),且 handler 自身没包 defer,照样漏捕
- 避免在 recover 分支里做耗时操作(如调用外部 HTTP 接口、写磁盘文件),否则该 goroutine 会被卡住,拖慢后续请求
goroutine 内 panic 必须在同 goroutine 里 recover
recover() 只对当前 goroutine 的 panic 生效。父 goroutine 无法代劳,跨 goroutine 调用 recover() 永远返回 nil。
立即学习“go语言免费学习笔记(深入)”;
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
常见错误现象:子 goroutine panic 后静默退出,导致 channel 阻塞、定时任务丢失、资源未释放,最终出现 fatal error: all goroutines are asleep - deadlock!。
- 每个
go func() { ... }()启动的逻辑,都得自己带defer func() { recover() }() - 不能把
defer写在panic之后(比如条件分支里),否则注册时机太晚,根本来不及捕获 - 如果 goroutine 启动后立即 panic(比如初始化失败),而
defer还没执行,recover 就失效——确保defer是函数体第一条语句
main 和 init 中的 panic 无法被 HTTP 层捕获
如果 panic 发生在 main() 函数里(比如 http.ListenAndServe 返回 error 没检查就直接 panic)或 init() 阶段(如配置加载失败),HTTP server 根本没起来,recover() 就没机会运行,进程直接退出。
- 在
main()开头加顶层defer func() { if r := recover(); r != nil { log.Fatalf("FATAL PANIC in main: %+v", r); os.Exit(1) } }() -
init()函数里绝不能panic——所有初始化逻辑(读配置、连 DB)必须用if err != nil判断并返回错误,否则服务起不来,运维查无可查 -
recover()后不要假设状态一致:栈已展开,变量可能部分更新、文件句柄未关闭、事务未回滚,此时唯一安全动作是记录、清理、退出
recover 不是 error 替代品,滥用反而掩盖 bug
panic 应只用于真正不可恢复的编程错误(如 nil 指针解引用、数组越界、类型断言失败),而不是业务错误(如用户输入非法、DB 查询无结果)。用 panic/recover 处理业务逻辑,等于把错误藏进黑盒。
- 第三方库内部 panic(如某些 JSON 解析器对非法 UTF-8 的处理)才需要你在调用侧加
recover——这是防御性兜底,不是常规流程 -
recover()性能比return error慢 1–2 个数量级,频繁触发会显著拖慢吞吐,绝不允许在高频路径(如数据库查询、HTTP body 解析)中包裹整个函数 - 打印堆栈要用
debug.PrintStack(),不是fmt.Sprintf("%+v", r)——后者只打 panic 值,不包含调用链,定位困难
最常被忽略的一点:recover 成功后,goroutine 并不回到 panic 发生点,而是从 defer 函数返回后继续往下走。这意味着你写的“降级逻辑”很可能在损坏的状态下运行——比如 map 已被部分写入、struct 字段值错乱、channel 已发送一半数据。它只适合做日志和退出,不适合做修复和重试。

















