panic

panic #

1、panic 中可以传任何值,不仅仅可以传 string

func main(){

    defer func(){
        if r := recover();r != nil{
            fmt.Println(r)
        }
    }()

   panic([]int{12312})
}
[12312]

在Go语言中,宕机(panic)是程序遇到无法继续执行的严重错误时触发的机制。正确处理panic可以防止程序意外崩溃,提高系统稳定性。

panic与recover基础 #

panic机制 #

panic是Go语言中处理不可恢复错误的机制,类似于其他语言的异常。当函数执行panic时:

  1. 当前函数停止执行
  2. 开始执行延迟函数(defer)
  3. 逐层向上返回,直到被recover捕获或程序崩溃
funcriskyFunction(){
    panic("something went wrong!")
}

recover机制 #

recover是用于捕获panic的内置函数,必须在defer函数中调用才有效

funcsafeFunction(){
    deferfunc(){
        if r :=recover(); r !=nil{
            fmt.Println("Recovered from panic:", r)
        }
    }()
    
    riskyFunction()
}

宕机恢复最佳实践 #

基本恢复模式 #

func ProtectedRun() {
    defer func() {
        if err := recover(); err != nil {
            log.Printf("Runtime panic caught: %v\n", err)
            // 可以在这里添加恢复逻辑或清理工作
        }
    }()
    
    // 可能触发panic的代码
    SomeBusinessLogic()
}

协程中的panic恢复 #

重要:每个goroutine都需要独立的recover机制,否则panic会导致整个程序崩溃。

func safeGoRoutine() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Goroutine recovered:", r)
        }
    }()
    
    // goroutine的业务逻辑
    panic("goroutine panic")
}

func main() {
    go safeGoRoutine()
    time.Sleep(time.Second)
}

获取panic堆栈信息 #

使用runtime包可以获取更详细的堆栈信息:

import "runtime/debug"

func ProtectedRun() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Printf("Panic: %v\nStack Trace:\n%s", err, debug.Stack())
        }
    }()
    
    // 业务代码
}

防止程序崩溃的策略 #

防御性编程 #

空指针检查

if somePtr == nil {
    return errors.New("nil pointer encountered")
}

数组/切片边界检查

if index >= 0 && index < len(slice) {
    value := slice[index]
    // 安全使用
}

类型断言检查

if str, ok := val.(string); ok {
    // 安全使用str
}

错误处理优于panic #

Go的哲学是显式错误处理优于异常,应尽量避免使用panic:

// 不好的做法
func Divide(a, b int) int {
    if b == 0 {
        panic("division by zero")
    }
    return a / b
}

// 好的做法
func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

HTTP服务的panic防护 #

func SafeHandler(handler http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if r := recover(); r != nil {
                log.Printf("Handler panic: %v", r)
                http.Error(w, "Internal Server Error", http.StatusInternalServerError)
            }
        }()
        handler(w, r)
    }
}

// 使用
http.HandleFunc("/", SafeHandler(myHandler))

长期运行的服务防护 #

func SupervisedGo(f func()) {
    go func() {
        defer func() {
            if r := recover(); r != nil {
                log.Printf("Restarting goroutine after panic: %v", r)
                // 可以添加延迟重启逻辑
                time.Sleep(time.Second)
                SupervisedGo(f)
            }
        }()
        f()
    }()
}

// 使用
SupervisedGo(myLongRunningTask)

高级防护模式 #

全局panic处理器 #

func SetGlobalPanicHandler() {
    // 捕获未处理的goroutine panic
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Global panic handler: %v\n%s", r, debug.Stack())
            // 可以选择优雅关闭或继续运行
        }
    }()
    
    // 主程序逻辑
    MainProgram()
}

优雅关闭机制 #

func main() {
    // 设置信号捕获
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
    
    // 设置panic处理
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Main panic: %v", r)
            ShutdownCleanup()
            os.Exit(1)
        }
    }()
    
    // 启动服务
    server := StartHTTPServer()
    
    // 等待信号或错误
    select {
    case sig := <-sigChan:
        log.Printf("Received signal: %v", sig)
    case err := <-server.ErrorChan():
        log.Printf("Server error: %v", err)
    }
    
    ShutdownCleanup()
}

性能与安全权衡 #

  • 不要过度使用recover:recover有一定的性能开销,只应在必要时使用
  • 关键路径避免panic:性能敏感路径应避免可能触发panic的操作
  • 测试panic场景:单元测试中应包含触发panic的测试用例

总结 #

  • panic用于真正不可恢复的错误,常规错误应使用error机制

  • 每个goroutine都需要独立的recover,否则会导致程序崩溃

  • 防御性编程,比事后恢复更重要

  • 关键服务应实现优雅恢复机制,而非直接崩溃

  • 记录详细的panic信息,有助于问题诊断