Go语言宕机恢复recover---防止程序崩溃
参考:
http://c.biancheng.net/view/64.html
关键点
希望通过下面的关键词
,能够实现目的
:能够快速
的回忆
,理解
,复习
知识点:
1、如何
使用
defer?
恢复recover
必须结合defer
才有效,单独
使用,没有
效果,2、
panic
类似于Java中的throw
抛出异常3、recover类似于
Java
中的try catch
,捕获
异常的4、恢复recover的
作用
?使得
宕机
中的goroutine恢复
过来, 让程序
在崩盘
时,继续执行
5、其他语言中,如Java,宕机,常常是以
异常
的形式
存在,如果有严重
的异常
没有捕获
到的话,就会宕机
Recover 是一个Go语言的内建
函数,可以让进入宕机流程中的 goroutine 恢复过来,recover 仅在延迟函数 defer
中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他
任何效果,如果当前的 goroutine 陷入恐慌,调用 recover
可以捕获到 panic
的输入值,并且恢复正常
的执行。
通常来说,不应该
对进入 panic 宕机的程序做任何处理
,但有时,需要我们可以从宕机中恢复
,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。
提示:
在其他语言里,宕机
往往以异常
的形式存在,底层
抛出异常,上层
逻辑通过try/catch
机制捕获异常,
没有
被捕获的严重异常
会导致宕机
,捕获的异常
可以被忽略
,让代码继续运行。
Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制。
1、让程序在崩溃时
继续执行
下面的代码实现了 ProtectRun() 函数,该函数传入一个匿名函数
或闭包后的执行函数,当传入函数以任何形式发生 panic 崩溃后,可以将崩溃发生的错误打印出来,同时允许后面的代码继续运行,不会造成整个进程的崩溃。
保护运行函数:
package main
import (
"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("运行后")
}
代码输出结果:
运行前
手动宕机前
error: &{手动触发panic}
赋值宕机前
runtime error: runtime error: invalid memory address or nil pointer dereference
运行后
对代码的说明:
-
第 9 行声明描述错误的结构体,保存执行错误的函数。
-
第 17 行使用 defer 将闭包延迟执行,当 panic 触发崩溃时,ProtectRun() 函数将结束运行,此时 defer 后的闭包将会发生调用。
-
第 20 行,recover() 获取到 panic 传入的参数。
-
第 22 行,使用 switch 对 err 变量进行类型断言。
-
第 23 行,如果错误是有 Runtime 层抛出的运行时错误,如空指针访问、除数为 0 等情况,打印运行时错误。
-
第 25 行,其他错误,打印传递过来的错误数据。
-
第 44 行,使用 panic 手动触发一个错误,并将一个结构体附带信息传递过去,此时,recover 就会获取到这个结构体信息,并打印出来。
-
第 57 行,模拟代码中空指针赋值造成的错误,此时会由 Runtime 层抛出错误,被 ProtectRun() 函数的 recover() 函数捕获到。
2、panic 和 recover 的关系
panic 和 recover 的组合
有如下特性:
-
有 panic 没 recover,程序宕机。
-
有 panic 也有 recover,程序不会宕机,执行完对应的 defer 后,从宕机点
退出当前函数
后继续执行
。
3、提示
虽然 panic/recover 能模拟其他语言的异常机制
,但并不建议
在编写普通函数时
也经常性
使用这种特性。
在 panic 触发的 defer 函数内
,可以继续调用 panic,进一步将错误外抛
,直到程序整体崩溃
。
如果想在捕获错误时
设置当前函数的返回值
,可以对返回值使用命名返回值方式
直接进行设置。