Go语言宕机恢复(recover)——防止程序崩溃
通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚可以将异常信息反馈到客户端,帮助调试。
提示
在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch 机制捕获异常,没有被捕获的严重异常会导致宕机,捕获的异常可以被忽略,让代码继续运行。Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制。
让程序在崩溃时继续执行
下面的代码实现了 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() 函数捕获到。
panic 和 recover 的关系
panic 和 recover 的组有如下特性:
有 panic 没 recover,程序宕机。
有 panic 也有 recover,程序不会宕机,执行完对应的 defer 后,从宕机点退出当前函数后继续执行。
提示
虽然 panic/recover 能模拟其他语言的异常机制,但并不建议在编写普通函数时也经常性使用这种特性。在 panic 触发的 defer 函数内,可以继续调用 panic,进一步将错误外抛,直到程序整体崩溃。
如果想在捕获错误时设置当前函数的返回值,可以对返回值使用名返回值方式直接进行设置。
- 随机文章
- 马尔代夫香港天气(马尔代夫和香港的天气情况对比)
- 去马尔代夫带什么(马尔代夫必备装备指南,出行前必看!)
- 女星 马尔代夫(女星马尔代夫游玩照片曝光)
- 文山马尔代夫酒吧(文山曼谷:在马尔代夫找到的惊艳酒吧)
- 新西兰马尔代夫湖(新西兰奥卡雷卡湖:探索南岛最美湖泊)
- 孝感马尔代夫拍照(夫妇在孝感打造马尔代夫式人妻度假照)
- 深圳飞往马尔代夫(深圳直飞马尔代夫:探假之旅开始了!)
- 阿狸 马尔代夫(阿狸游马尔代夫)
- 大连马尔代夫天气(大连气象台发布马尔代夫近期天气预报)
- 重庆 马尔代夫 航班(重庆至马尔代夫开通直飞航线)
- 渭南马尔代夫温泉(马尔代夫温泉来了!渭南这里开放了!)
- 新手马尔代夫攻略(马尔代夫入门指南,带你畅游梦幻海岛)
- 蔡甸 马尔代夫(蔡甸居民赴马尔代夫进行深度游学)
- 新加坡加马尔代夫(新马岛一体化!新马岛联合旅游推广!)
- 撑伞图片马尔代夫(美轮美奂!漩涡中的马尔代夫一伞独行)
- 玉林 马尔代夫(马代游客涌入玉林,旅游经济迎来新契机)
- 马尔代夫中国赛区(马尔代夫成为中国电竞赛区新热门地点)
- 颜色 马尔代夫(蓝色海洋里的浪漫之旅:探秘马尔代夫)
- 测评马尔代夫酒店(测评马尔代夫度假村:寻找你的天堂。)
- 慈利 马尔代夫(慈利旅游企业将为游客提供马尔代夫度假套餐)
- 马尔代夫休假视频(旅游天堂:马尔代夫度假胜地视频欣赏)
- 戴睿 马尔代夫(戴睿:探寻马尔代夫海底世界的奇妙之旅)
- 长滩 马尔代夫(长滩与马尔代夫:两个完美的热带度假胜地)
- 邯郸 马尔代夫(邯郸市民推荐的马尔代夫旅游胜地)
- 马尔代夫从哪里飞(马尔代夫出发:最佳出发地点在哪里?)
- 张瀚 马尔代夫(张瀚加入马尔代夫旅游局,将担任中国区首席代表)
- 草坝 马尔代夫(马尔代夫一处美不胜收的草地酒店——草坝)
- 马尔代夫个人养殖(马尔代夫推广个人养殖,带动经济发展)
- 马尔代夫六一旅游(六一嗨玩在马尔代夫,亲子游的好去处)
- 瓦度 马尔代夫(后的:瓦度岛:探寻马尔代夫最美丽的度假胜地)
