Go 中未捕获的 panic 仅终止当前 goroutine,主 goroutine 或 HTTP handler 中 panic 会导致进程退出;recover 必须在 defer 函数体内且 defer 要在 panic 前注册,子 goroutine 的 panic 无法被外部 recover 捕获,每个 goroutine 需独立封装 defer+recover。

Go 程序不会因为单个 panic 就“全局崩溃”,但未捕获的 panic 会导致**当前 goroutine 终止**,若发生在主 goroutine 或 HTTP handler 主流程中,服务会直接退出——这不是语言缺陷,而是你没在正确位置放 recover()。
recover 必须写在 defer 函数体内,且 defer 要在 panic 前注册
常见错误是把 defer 放在 panic 后面,或者写成 defer recover()。这两种写法都无效:
-
defer recover():会立即执行recover()(此时还没 panic),返回nil,之后 panic 依然传播 -
panic("x"); defer func() { recover() }():defer语句根本没机会注册,panic 已触发栈展开 - 正确顺序必须是:
defer func() { if r := recover(); r != nil { ... } }(); panic("x")
HTTP handler 中的 panic 必须在 handler 内部 recover
Web 服务里最常见的崩溃场景是:handler 主流程 panic,但中间件或路由层没包 recover,导致整个进程退出。不能指望“统一兜底”——中间件的 defer 只对它自己 goroutine 里的 panic 有效。
- 用中间件封装时,
recover()必须在next.ServeHTTP()的 defer 里,而不是外层函数 - 不要在 handler 里起 goroutine 后就不管了:
go func() { panic("x") }()—— 这个 panic 永远不会被 handler 的 defer 捕获 - 如果业务逻辑需要异步执行,每个 goroutine 都要自带
defer+recover,例如:go func() { defer func() { recover() }(); doWork() }()
goroutine 内 panic 无法被外部 recover 捕获
这是最常被忽略的点:主线程、HTTP handler、甚至 errgroup 外层的 recover(),对子 goroutine 里的 panic 完全无效。子 goroutine panic 后静默退出,可能造成任务丢失、资源泄漏、状态不一致。
- 每个长期运行的 worker goroutine(比如从 channel 消费消息)都必须独立封装
recover() - 恢复后别继续执行原逻辑:数组越界后
arr[i]已失败,recover()不会修复内存状态,应记录日志后return或发信号退出 - 若需重启,用显式控制的封装函数(如带重试计数的
recoverer(5, id, f)),避免递归启动失控
真正难的不是写 recover(),而是判断该不该 recover:可预期的错误(文件不存在、网络超时)必须用 error 返回;只有第三方库不可控行为、插件沙箱、守护进程保活等场景才适合用 recover()。写多了,反而掩盖真正该修复的 bug。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











