recover必须在每个goroutine内部显式部署,仅对当前协程生效;HTTP中间件的recover只保handler入口,子协程需各自加defer+recover;recover不回滚副作用,需手动清理资源并避免二次panic;应联动熔断器与优雅关闭。

recover必须在每个goroutine内部显式部署
Go的recover只对当前goroutine生效,绝不会跨协程传播或捕获。你在main函数里加defer func() { recover() }(),对HTTP handler、定时任务、WebSocket连接协程里的panic完全无效——那些协程会直接崩溃,可能拖垮整个服务。
真正起作用的写法是:每个独立启动的goroutine,都必须在函数体第一行就注册defer + recover。不是“写一次,全局生效”,而是“每个协程,各自兜底”。
go func() { defer func() { if r := recover(); r != nil { log.Printf("panic in worker: %v", r) } }(); doWork() }()- 用
sync.WaitGroup管理的批量worker,每个worker函数开头都要有同样的defer+recover - 第三方库启动的协程(如
http.Server.Serve、time.Ticker.C消费循环)也必须手动包一层,不能依赖外层
HTTP中间件里的recover不是“全局”,而是“每请求一实例”
Gin的gin.Recovery()、Echo的middleware.Recover()看起来像统一拦截,实际原理很简单:它们把defer func() { recover() }()插在每个HTTP handler执行链的最外层。也就是说,每个请求进来时,都会在一个新的goroutine中跑这个handler,而该goroutine自带防护。
但要注意:如果handler内部又启了子goroutine(比如异步发消息、后台清理),那个子协程仍需自己加defer+recover,否则panic照样导致子协程退出,还可能泄漏资源。
立即学习“go语言免费学习笔记(深入)”;
- 错误示范:
go func() { riskyParse(data) }()—— 没包defer,panic后无法捕获 - 正确做法:
go func() { defer func() { if r := recover(); r != nil { log.Error(r) } }(); riskyParse(data) }() - 框架中间件只保到handler入口,不保handler内部spawn的所有子协程
recover后不能“假装没事”,必须处理副作用
recover只停止panic传播,它不撤销任何已发生的操作。文件已打开、channel已发送、数据库事务已提交——这些都不会回滚。如果你在recover后继续用一个已被close的chan,下一次写入就会再次panic。
典型陷阱是状态错乱:比如在解析JSON时panic,但之前已往某个map里写了部分字段,recover后继续返回这个半成品map,调用方可能因字段缺失panic二次崩溃。
- 建议模式:recover后立即记录完整堆栈(
debug.Stack())、关闭关键资源(如file.Close())、返回明确错误,而不是继续执行业务逻辑 - 避免在recover块里调用可能再次panic的函数(如
json.Marshal未校验输入) - 不要把recover用在循环体内部做“跳过单条失败数据”——这容易掩盖并发写map、空指针等本该暴露的问题
recover不是熔断器,配合失败计数才能防雪崩
单纯recover捕获panic,只是让程序不断重启同一条出错路径。高频panic往往意味着底层依赖异常(DB连不上、下游超时、配置错乱),此时应触发熔断,而非反复重试。
你需要把recover和熔断状态联动:每次recover成功,调用breaker.MarkFailed();入口处先if !breaker.Allow() { return errCircuitBreakerOpen }。否则recover只是把崩溃延迟了几毫秒,最终仍是连接堆积、goroutine泄漏、内存OOM。
- 别用
hystrix-go.Do——它不recover,panic会冒泡;改用DoC,它内部做了recover并转成可判断的*hystrix.Error - 熔断器状态必须用
sync/atomic或sync.RWMutex保护,否则并发更新会导致状态错乱 - recover捕获后,应触发优雅shutdown(如
srv.Shutdown()),而不是硬扛——持续panic的服务比短暂不可用更危险


















