Golang中如何处理未捕获的panic

Golang中如何处理未捕获的panic 我需要一种机制,使得任何未处理的恐慌(来自Go代码)不会导致我的应用程序进程崩溃。目前,我的Go代码引发的任何下游恐慌都会冻结/崩溃我的应用程序。我如何优雅地处理所有恐慌,并继续为我的客户端应用程序提供服务?

3 回复

谢谢。我会的。

更多关于Golang中如何处理未捕获的panic的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在你的主程序“循环”中,使用常规的延迟机制来处理恐慌、关闭和重启。

或者,直接让程序崩溃,由你的监控进程来处理重启。

在Go中处理未捕获的panic,可以通过以下两种主要方式实现:

1. 使用recover()在goroutine顶层捕获

在每个goroutine的入口函数中使用deferrecover()

func safeGoRoutine() {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Recovered from panic: %v", r)
            // 记录堆栈信息
            debug.PrintStack()
        }
    }()
    
    // 你的业务逻辑
    riskyOperation()
}

func main() {
    go safeGoRoutine()
    // 主goroutine继续运行
    select {}
}

2. 创建全局panic处理器包装所有goroutine

type PanicHandler struct{}

func (p *PanicHandler) Run(f func()) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Panic recovered: %v\nStack trace:\n%s", 
                r, string(debug.Stack()))
            // 可以在这里添加监控上报
        }
    }()
    f()
}

func main() {
    handler := &PanicHandler{}
    
    // 启动多个受保护的goroutine
    for i := 0; i < 10; i++ {
        go handler.Run(func() {
            // 你的业务代码
            processRequest(i)
        })
    }
    
    // 主goroutine也需要保护
    go handler.Run(func() {
        mainBusinessLogic()
    })
    
    select {}
}

3. 使用中间件模式处理HTTP服务panic

对于HTTP服务器,可以创建恢复中间件:

func recoveryMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("HTTP panic recovered: %v\n%s", 
                    err, string(debug.Stack()))
                
                w.WriteHeader(http.StatusInternalServerError)
                json.NewEncoder(w).Encode(map[string]string{
                    "error": "Internal server error",
                })
            }
        }()
        
        next.ServeHTTP(w, r)
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
        // 可能panic的业务代码
        panic("something went wrong")
    })
    
    // 使用恢复中间件包装
    protectedMux := recoveryMiddleware(mux)
    
    log.Println("Server starting on :8080")
    http.ListenAndServe(":8080", protectedMux)
}

4. 监控和重启策略

对于关键服务,可以结合监控和重启:

func supervise(work func()) {
    for {
        func() {
            defer func() {
                if r := recover(); r != nil {
                    log.Printf("Supervised goroutine crashed: %v", r)
                    // 等待后重启
                    time.Sleep(1 * time.Second)
                }
            }()
            work()
        }()
    }
}

func main() {
    go supervise(func() {
        // 你的业务逻辑
        runService()
    })
    
    // 主goroutine保持运行
    select {}
}

重要注意事项:

  1. 资源清理:确保在recover后正确释放资源
  2. 状态一致性:panic后的状态可能不一致,需要适当处理
  3. 不要静默忽略:至少记录panic信息用于调试
  4. HTTP连接:对于HTTP服务,确保在panic后仍能正确关闭响应

这些方法可以防止panic导致整个进程崩溃,但需要根据具体业务场景选择合适的恢复策略。

回到顶部