recover仅对当前goroutine的defer内调用有效,不能防止panic发生,只能截断其传播;HTTP handler、中间件、子goroutine、worker pool等均需显式添加defer+recover,main/init中panic无法被HTTP层捕获。

recover 不能防止 panic,只能防止 panic 导致服务整体不可用;它只在 defer 函数内调用才有效,且仅对当前 goroutine 生效。
HTTP handler 中必须手动加 defer+recover
标准库 http.ServeHTTP 内部确实有 recover,但它只打印日志、不写响应,结果是客户端收到空响应或超时,而服务还在跑——这不是“没崩溃”,是“请求已死”。
- 每个 handler 或中间件入口必须显式写
defer func() { if r := recover(); r != nil { /* 日志 + http.Error */ } }() - 中间件的
defer必须包裹next.ServeHTTP(w, r)调用,否则 panic 发生在next外部就捕不到 - 别依赖“全局中间件”:如果业务 handler 自己没包
defer,panic 仍会漏掉 -
recover后不要继续处理请求逻辑,状态可能已损坏(比如部分写入的 response body、未关闭的数据库连接)
子 goroutine 的 panic 必须自己 recover
父 goroutine 的 defer 对子 goroutine 完全无效。常见现象是 go func() { panic("boom") }() 静默退出,导致 channel 阻塞、定时任务丢失、最终触发 fatal error: all goroutines are asleep - deadlock!。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 每个
go func() { ... }()启动的逻辑,都得自己带defer func() { recover() }() - 封装
safeGo(fn)时,必须确保fn内部也受保护,不能只靠外层包装 - worker pool 每个 worker goroutine 都要独立加
defer+recover,不是启动它的那层负责 -
defer必须是 goroutine 函数体的第一条语句,否则 panic 可能发生在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判断并返回错误 -
panic传error类型值,而不是裸字符串,方便结构化日志、加 traceID、对接监控告警
recover 后别做耗时或高风险操作
recover() 成功后,goroutine 不会回到 panic 点,而是从 defer 函数返回后继续执行。此时程序状态不可信,尤其引用类型(map、slice、指针)可能已被部分修改。
- recover 分支里只做日志记录和清理(如关闭文件、释放锁),不要调用外部 HTTP 接口、写磁盘、解包未校验 JSON
- 避免在 recover 分支里再次触发 panic(比如访问可能为 nil 的接口)
- 别用
panic/recover替代error处理:用户输入非法、DB 查询失败、网络超时等,都该走if err != nil,不是兜底场景 - 频繁 panic+recover 性能差 1–2 个数量级,因为涉及栈展开和 runtime 干预
最易被忽略的一点:recover 不是“异常捕获”,它是 goroutine 级别的 panic 截断机制,且仅在 defer 函数中有效。写错位置、跨 goroutine 使用、或在 recover 后继续使用可疑状态,都会让兜底失效。

















