HTTP中间件中的recover只对当前请求有效,因为每个请求在独立goroutine中执行,defer+recover仅捕获本goroutine的panic;子goroutine需自行recover,且recover后必须手动清理资源、回滚事务、记录堆栈。

HTTP中间件里recover为什么只对当前请求有效
因为每个HTTP请求都在独立的goroutine中执行,中间件里的defer func() { recover() }()注册在该goroutine内,仅能捕获本goroutine的panic。它不是“全局”捕获,只是“每请求一捕获”。
- 子goroutine(比如
go func() { panic("x") }())的panic完全不会传播到父goroutine,中间件recover对其无效 - 如果handler里启动了后台任务(如异步日志、定时清理),那些goroutine必须自己包
defer+recover - 中间件无法覆盖
http.Server启动前或监听循环中的panic,这类错误需在http.ListenAndServe外层再套一层recover
recover写错位置就等于没写
recover()必须出现在defer注册的函数体内,且该defer必须在panic发生前已注册——顺序和作用域都关键。
- ❌
if r := recover(); r != nil { }直接写在函数体里 → 恒返回nil - ❌
panic("boom"); defer func() { recover() }()→defer根本没机会执行 - ✅ 正确姿势:函数开头就写
defer func() { if r := recover(); r != nil { /* 处理 */ } }() - ⚠️ 如果函数内有多个panic可能点(比如不同分支调用第三方库),这个
defer必须能覆盖全部执行路径
recover后状态可能已损坏,不能“假装没事”
recover()只停止panic传播,不撤销任何副作用:已打开的文件、已发送的channel消息、已提交的事务都不会回滚。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 数据库事务中panic后,必须显式
tx.Rollback(),否则数据不一致 - 已
close()的channel再写入会触发二次panic,recover后要检查资源状态再操作 - 推荐模式:记录
debug.Stack()、清理关键资源(如关闭file、rollback tx)、返回明确error,而不是继续执行原逻辑 - 日志里只记
r不够,必须包含堆栈,否则根本定位不到panic源头
所有go语句都得自己带recover,没有例外
你加了HTTP中间件,服务看起来稳了,但后台goroutine每次panic都静默泄漏——没日志、没告警、没回收,比崩溃更危险。
立即学习“go语言免费学习笔记(深入)”;
- 每个
go func() { ... }()内部必须自己写defer func() { recover() }() - 手动重复写太容易漏,建议封装
safeGo:内部自动包一层defer+recover,并统一上报 - RPC客户端、定时器回调、WebSocket读写协程……只要启了新goroutine,就得过这一关
- 别依赖“应该没人panic”,生产环境里第三方库、空指针、数组越界、类型断言失败都可能随时触发
真正难的不是写recover,而是确保它出现在每一个可能出错的goroutine入口;最容易被忽略的,是recover之后那几行资源清理代码——它们决定系统是“恢复”还是“苟延残喘”。

















