recover必须在defer中调用且仅对当前goroutine有效,HTTP handler需通过中间件统一包裹才能捕获panic,直接在handler内使用无法阻止连接关闭、日志丢失或空响应。

recover在HTTP处理函数里根本不起作用
Go的recover只能捕获当前goroutine中由panic触发的异常,而HTTP服务器启动后,每个请求都在独立goroutine中执行。如果你直接在http.HandlerFunc里写defer recover(),它确实能运行,但**不会阻止HTTP连接被关闭、日志丢失、或返回空响应**——因为标准库的net/http在handler panic后会直接终止该goroutine,不等待你处理。
必须用中间件包裹handler才能真正捕获panic
正确做法是把recover逻辑提前到请求进入业务handler之前,也就是写一个中间件,在调用下游handler前加defer。常见错误是只在某个具体handler里加recover,结果其他路由照样崩溃。
- 中间件要对所有路由生效,推荐用
http.HandleFunc包装或http.ServeMux统一注册 -
defer func() { if r := recover(); r != nil { /* 记录日志、返回500 */ } }()必须放在handler函数最开头 - 不要忽略
recover()返回值类型:可能是string、error或自定义结构体,建议统一转成fmt.Sprintf("%v", r) - 注意:
recover()只对本goroutine有效,别试图在子goroutine里recover主goroutine的panic
panic时如何安全返回HTTP响应
handler panic后,ResponseWriter可能已部分写入或处于不可写状态。直接调用w.WriteHeader(500)或w.Write有风险,尤其当底层连接已被关闭。
OpenClaw 基础设施心跳监控与自动恢复:部署/卸载心跳守护进程,配置主机监控(Gateway、磁盘、内存、CPU、进程),设置飞书告警,自动重启故障Gateway。适用关键词:心跳监控、heartbeat daemon、服务器告警、自动重启、健康检查、auto recovery。
- 先检查
w是否实现了http.Hijacker或http.CloseNotifier(基本不用),更实际的是用if !w.Header().WasWritten()判断是否还能写header - 写响应体前,用
defer func() { ... }()确保即使日志记录出错也不影响主流程 - 示例:
func recoverMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if r := recover(); r != nil { log.Printf("Panic at %s %s: %v", r.Method, r.URL.Path, r) if !w.Header().WasWritten() { w.WriteHeader(http.StatusInternalServerError) } io.WriteString(w, "Internal Server Error") } }() next.ServeHTTP(w, r) }) }
recover无法捕获的崩溃场景
别指望recover能兜住所有问题。以下情况它完全无效:
-
os.Exit()、syscall.Exit():进程直接退出,不经过defer链 - 协程泄漏导致OOM:内存耗尽时程序被系统kill,不是panic
- 死锁(如channel无缓冲且无人接收):程序卡住,不panic也不返回
- 第三方库调用C代码时发生的段错误(SIGSEGV):Go runtime不捕获信号,需用
signal.Notify单独处理
真正健壮的服务得靠监控+超时+限流+健康检查,recover只是最后一道补救,不是容错主力。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










