使用Golang的recover机制防止程序崩溃

发表时间: 2023-10-26 10:27

宕机恢复(recover)--防止程序崩溃

Recover 是一个Golang的内建函数, 可以让进入宕机流程中的 goroutine 恢复过来, recover 仅在延迟函数 defer 中有效, 在正常的执行过程中, 调用 recover 会返回 nil 并且没有其他任何效果, 如果当前的 goroutine 陷入恐慌中, 调用 recover 可以捕获到 panic 的输入值, 并且恢复正常的执行。

通常来说, 不应该对进入 panic 宕机的程序做任何处理, 但有时, 需要我们可以从宕机中恢复, 至少我们可以在程序崩溃前, 做一些操作, 举个例子,

当 web 服务器遇到不可预料的严重问题时, 在崩溃前应该将所有的连接关闭, 如果不做任何处理, 会使得客户端一直处于等待状态, 如果 web 服务器还在开发阶段, 服务器甚至可以将异常信息反馈到客户端, 帮助调试。

在Golang中,没有恢复的恐慌会导致进程中断, 因此我最终将以下代码片段放在每个函数的开头:

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

案例: 让程序在崩溃时继续执行

package mainimport ("fmt""runtime")// 崩溃时需要传递的上下文信息type panicContext struct {function string // 所在函数}// 保护方式允许一个函数func ProtectRun(entry func()) {// 延迟处理的函数defer func() {// 发生宕机时,获取panic传递的上下文并打印err := recover()switch err.(type) {case runtime.Error: // 运行时错误fmt.Println("runtime error:", err)default: // 非运行时错误fmt.Println("error:", err)}}()entry()}func main() {    fmt.Println("运行前")    // 允许一段手动触发的错误    ProtectRun(func() {        fmt.Println("手动宕机前")        // 使用panic传递上下文        panic(&panicContext{        "手动触发panic",        })        fmt.Println("手动宕机后")    })    // 故意造成空指针访问错误    ProtectRun(func() {        fmt.Println("赋值宕机前")        var a *int        *a = 1        fmt.Println("赋值宕机后")    })    fmt.Println("运行后")}

附: 空指针正确的语法

package mainimport (    "fmt")func main() {    var a *int    var b int    a = &b //a指向b    *a = 1    fmt.Println(a)}

一般defer recover这种机制经常用在常驻进程的应用, 比如Web服务, 在Go里面, 每一个Web请求都会分配一个goroutine去处理, 在没有做任何处理的情况下, 假如某一个请求发生了panic, 就会导致整个服务挂掉, 这是不可接受的,

所以在Web应用里面必须使用recover保证即使某一个请求发生错误也不影响其它请求。

注意: 虽然 panic/recover 能模拟其他语言的异常机制, 但并不建议在编写普通函数时也经常性使用这种特性。