defer、panic与recover

既然都 panic 了,不就是想让程序崩溃吗?为什么还要“恢复”它?

panic 不一定是“程序该死”,更多时候是“当前请求/任务该死”。

我们可以把程序分为两种崩溃模式:

1. 致命错误 —— 不应该 recover

例如:配置文件缺失、数据库连接失败、关键初始化逻辑出错。

这种错误发生后,程序已无法继续正常运行,panic不应该 recover,让程序崩溃退出是正确的。

2. 局部错误 —— 应该 recover

很多 HTTP/RPC 服务、goroutine 子任务中,panic 只代表当前请求或子任务出错,而不代表整个进程该死

例如:

  • 一个 HTTP 请求的处理函数里数组越界 panic
  • 一个 goroutine 里发生了未预期的空指针

如果不用 recover

  • 整个进程崩溃 → 服务停止 → 影响所有用户

如果用 recover

  • 该请求返回 500 错误
  • 主进程继续运行,其他请求不受影响

示例对比

没有 recover(错误)

1
2
3
4
5
func handler(w http.ResponseWriter, r *http.Request) {
    var arr []int
    _ = arr[1] // panic
}
// 一个请求出错 → 整个程序崩溃

有 recover(正确)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func handler(w http.ResponseWriter, r *http.Request) {
    defer func() {
        if err := recover(); err != nil {
            log.Printf("panic: %v", err)
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte("internal error"))
        }
    }()
    
    var arr []int
    _ = arr[1] // panic 被捕获,该请求返回 500,进程继续
}

总结:什么时候 recover?

场景是否 recover原因
初始化失败程序无法运行
HTTP 请求内 panic只影响单个请求
goroutine 任务防止进程崩溃
明确不该出现的 bug看情况开发时可崩溃,生产环境建议 recover + 记录日志
  • panic 代表“当前逻辑无法继续执行”,但不代表“整个进程必须死”。
  • recover 是在进程层面保护边界(请求边界、任务边界),让错误可控、服务保持可用。
Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计