recover不能防止程序崩溃,仅能恢复当前goroutine执行,必须在defer中调用且需记录堆栈、上下文;子协程需单独recover;它不是error替代品,90%的panic本是可预防的bug。

recover 不能防止程序崩溃,它只能让当前 goroutine 从 panic 中恢复执行——但前提是:你已经在正确的 goroutine、正确的位置、用正确的方式调用了它。线上服务“突然挂了”,八成不是没写 recover,而是只在主协程写了,漏了所有子协程;或者写了,但没打堆栈、没记上下文,问题来了还是两眼一抹黑。
recover 必须写在 defer 函数里,且仅对当前 goroutine 有效
这是最常踩的坑:recover() 放在普通代码块里等于没写;放在其他函数里(比如单独抽一个 handlePanic())也捕获不到。它必须是 defer func() { recover() }() 这种结构。
-
recover()在非 panic 状态下返回nil,不报错也不提醒,静默失效 - 每个 goroutine 的 panic 是隔离的——
main里defer recover()拦不住go func(){ panic(...) }() - HTTP handler 中 recover 了,但如果 handler 里又起了 goroutine 去处理消息,那个 goroutine 还是会直接炸掉进程
HTTP 服务必须在中间件层加 recover,但别只打 err
单靠 http.Error(w, "internal error", 500) 不够。你得知道是哪个请求、哪个路由、哪个 traceID 触发了 panic,否则日志里只有一行 panic: runtime error: index out of range,毫无定位价值。
- 务必在
recover()后调用debug.Stack()或runtime.Stack(buf, false)获取完整调用栈 - 把
r.URL.Path、r.Header.Get("X-Trace-ID")、r.RemoteAddr一并记进日志 - 不要在 recover 后继续返回业务数据——
recover不等于“修复”,只是“止损”。状态可能已损坏,应清空响应、关闭连接、返回 500
所有 go 关键字启动的地方,都要包一层 GoSafe
裸写 go func(){}() 是线上 panic 泄漏的头号源头。你没法靠 code review 拦住所有人,只能收口启动逻辑。
- 封装类似这样的函数:
GoSafe("order-callback", func(){ ... }),内部自动defer recover()+ 打栈 + 带命名 - 命名很重要:
"order-callback"比"worker"更容易关联监控和日志 - 避免在 GoSafe 里做重试或兜底业务逻辑——recover 只负责不崩,业务容错要靠超时、断路器、重试策略等独立机制
recover 不是 error 替代品,90% 的 panic 场景本不该发生
数组越界、空指针解引用、除零、手动 panic("config missing")……这些都不是“异常”,是 bug。靠 recover 掩盖,只会让问题更难暴露、更晚发现。
- 输入校验、切片边界检查、指针判空,该写的 if 一条都不能少
- 第三方库调用前,先看它文档是否声明会 panic;如果会,就用
recover包一层,而不是假设它一定安全 - 测试里主动触发 panic 场景(比如传空字符串给必填字段),验证 recover 是否真生效、日志是否可查
真正难的从来不是加几行 defer recover(),而是判断哪里该加、加了之后怎么确保现场可追溯、以及——加完之后,敢不敢删掉那些本不该存在的 panic。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











