每个 goroutine 的 panic 必须在自身内用 defer+recover 捕获,否则协程静默退出;主进程崩溃仅由主 goroutine 未 recover 的 panic 或子协程 panic 引发的资源泄漏/状态不一致导致。

每个 goroutine 的 panic 都必须在它自己的上下文中用 recover 捕获,否则会静默终止该协程,但不会直接导致主进程崩溃;真正让主进程退出的,是**未被 recover 的主 goroutine panic**,或**子 goroutine panic 后引发资源泄漏/状态不一致,间接拖垮服务**。
goroutine 内必须单独加 defer+recover
Go 不会跨 goroutine 传播 panic,也不会自动为子协程兜底。你起一个 go f(),里面 panic 了,若没写 defer + recover,这个 goroutine 就直接退出,defer 里没执行的清理逻辑(比如关文件、释放锁、归还连接池)全丢掉。
- 必须把
defer func() { if r := recover(); r != nil { /* 处理 */ } }()放在每个可能 panic 的 goroutine 函数最开头 - 不要指望外层函数的
recover能捕获子 goroutine 的 panic —— 它们是隔离的 - 如果业务逻辑封装在函数里(比如
handleRequest),就在该函数内部加 recover,而不是只在启动 goroutine 的地方加
recover 只在 defer 函数里有效,且仅对当前 goroutine 生效
recover 是个内置函数,但它只有在 defer 声明的函数中调用才起作用;而且它只能捕获「当前 goroutine 正在展开的 panic」,不能跨 goroutine 抓。
- 下面这段代码无效:
go func() { recover() }()—— 没 defer,recover 返回 nil - 下面这段也无效:
defer recover()—— recover 必须在函数体里调用,不能直接 defer 一个裸调用 - 正确写法只有一种模式:
defer func() { if r := recover(); r != nil { /* 日志 or 转 error */ } }() - recover 返回
interface{},建议用fmt.Sprintf("%+v", r)打印,保留堆栈信息
panic 不该替代 error,但 recover 可用于兜底不可控场景
滥用 panic 处理业务错误(比如参数校验失败、HTTP 400)会让错误边界模糊,增加 recover 维护成本;但第三方库空指针、反射调用崩溃、插件动态加载失败这类不可预测问题,就适合用 recover 拦住。
- HTTP handler 中推荐加 recover 中间件,防止单个请求 panic 导致整个 server 退出
- 数据库查询前做了
if err != nil判断,就不该再对Scanpanic —— 那是库 bug,应 recover 并上报 - recover 里避免耗时操作:别发 HTTP 请求、别写大日志文件、别等 channel —— 会卡住整个 goroutine
- recover 后建议把 panic 转成
error,通过 channel 或回调通知主流程,而不是默默吞掉
最容易被忽略的是:main 函数和 init 阶段的 panic 无法被 HTTP 层或 goroutine 级别的 recover 捕获,必须在 func main() 最外层显式加 defer+recover —— 否则任何初始化失败都会让进程直接退出。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











