真正稳的异常捕获是分层拦截+显式错误传导+关键panic隔离;gpool.get/put必须检查error,mustput/mustget本质是转panic,仅适用于测试或初始化;全局recover无法捕获子goroutine panic,需在每个go内手动defer recover;panic仅用于不可恢复的灾难场景,其余一律走error。

Go 框架里想靠 recover 一招吃遍天下?不行。真正稳的异常捕获不是“兜底”,而是分层拦截 + 显式错误传导 + 关键 panic 隔离。
gpool.Put/Get 返回错误必须检查,别信“池还活着”
GoFrame 的 gpool.Pool 不是黑盒,Get 和 Put 都明确返回 error,且常见错误类型直接暴露状态:
-
gcode.CodeInvalidOperation:池已调用Close,后续所有操作都失败 -
gcode.CodeInternalError:对象创建函数NewFunc执行出错(比如 DB 连接失败) -
nil错误不等于安全——它只代表本次操作没触发显式异常,但对象本身可能已损坏
错误不检查,就等于把资源泄漏、空指针、连接复用失败全扔给上层猜。
MustPut/MustGet 不是“更安全”,是主动把错误转成 panic
MustPut 源码本质就是:if err != nil { panic(err) }。它不解决异常,只是换种方式暴露问题:
- 适合测试或初始化阶段——你希望立刻崩,而不是静默失败
- 线上业务中滥用会导致本可恢复的错误(如临时连接超时)直接中断 goroutine
- 若真要用,必须配
defer func() { recover() }(),且 recover 后要判断错误类型再决定是重试、降级还是打日志上报
别把它当“增强版 Put”,它是调试开关,不是生产防护罩。
全局 recover 只能兜住 main goroutine,协程里的 panic 会直接消失
在 main 函数或 HTTP handler 入口加 defer recover,只能捕获当前 goroutine 的 panic:
- goroutine 内部起的新 goroutine(比如异步写日志、后台清理)panic 后不会被主 recover 捕获
- HTTP 中间件的 recover 对每个请求 goroutine 有效,但对
go func() {}()启动的子 goroutine 无效 - 真正要保协程,得在每个
go语句内部自己包一层defer recover,例如:go func() { defer func() { if r := recover(); r != nil { glog.ErrorStack(r) } }() // 你的异步逻辑 }()
漏掉任意一个 goroutine,就等于留了一个静默崩溃点。
panic 不该出现在业务逻辑里,除非你确定无法继续
Go 的设计哲学是:95% 的错误走 error 返回,5% 的灾难走 panic。但现实里常看到:
- 数据库查询返回
err != nil,却直接panic(err)—— 这属于拒绝服务,不是容错 - 配置文件缺失就 panic,而不是 fallback 到默认值或返回
ErrConfigMissing - 类型断言失败后没判
ok就解引用,触发 runtime panic —— 这是 bug,不是异常处理
真正该 panic 的场景极少:程序启动时核心依赖彻底不可用(如加密密钥文件损坏)、内存分配器异常、框架底层断言失败。其余一律走 error 分支。
最易被忽略的一点:recover 只能捕获当前 goroutine 的 panic,且一旦 recover,原 panic 的堆栈就丢失了——如果没在 recover 里立刻调用 runtime/debug.PrintStack() 或记录完整 trace,你就失去了定位根因的唯一线索。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











