学习与交流:Go语言技术微信群
商务合作加微信:LetsFeng
学习与分享:GoLand2022 正版激活码 全家桶通用版
课本,文档学习Go语言,个人强烈推荐这本书
现在就开始你的Go语言学习之旅吧!人生苦短,let’s Go.
Recover
是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine
恢复过来,recover
仅在延迟函数 defer
中有效,在正常的执行过程中,调用 recover
会返回 nil
并且没有其他任何效果,如果当前的 goroutine
陷入恐慌,调用 recover
可以捕获到 panic
的输入值,并且恢复正常的执行。
通常来说,不应该对进入 panic
宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web
服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。
提示
在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch
机制捕获异常,没有被捕获的严重异常会导致宕机,捕获的异常可以被忽略,让代码继续运行。
Go语言没有异常系统,其使用 panic
触发宕机类似于其他语言的抛出异常,recover
的宕机恢复机制就对应其他语言中的 try/catch
机制。
让程序在崩溃时继续执行
下面的代码实现了 ProtectRun()
函数,该函数传入一个匿名函数或闭包后的执行函数,当传入函数以任何形式发生 panic
崩溃后,可以将崩溃发生的错误打印出来,同时允许后面的代码继续运行,不会造成整个进程的崩溃。
保护运行函数:
1package main
2
3import (
4 "fmt"
5 "runtime"
6)
7
8// 崩溃时需要传递的上下文信息
9type panicContext struct {
10 function string // 所在函数
11}
12
13// 保护方式允许一个函数
14func ProtectRun(entry func()) {
15
16 // 延迟处理的函数
17 defer func() {
18
19 // 发生宕机时,获取panic传递的上下文并打印
20 err := recover()
21
22 switch err.(type) {
23 case runtime.Error: // 运行时错误
24 fmt.Println("runtime error:", err)
25 default: // 非运行时错误
26 fmt.Println("error:", err)
27 }
28
29 }()
30
31 entry()
32
33}
34
35func main() {
36 fmt.Println("运行前")
37
38 // 允许一段手动触发的错误
39 ProtectRun(func() {
40
41 fmt.Println("手动宕机前")
42
43 // 使用panic传递上下文
44 panic(&panicContext{
45 "手动触发panic",
46 })
47
48 fmt.Println("手动宕机后")
49 })
50
51 // 故意造成空指针访问错误
52 ProtectRun(func() {
53
54 fmt.Println("赋值宕机前")
55
56 var a *int
57 *a = 1
58
59 fmt.Println("赋值宕机后")
60 })
61
62 fmt.Println("运行后")
63}
代码输出结果:
运行前
手动宕机前
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
,进一步将错误外抛,直到程序整体崩溃。
如果想在捕获错误时设置当前函数的返回值,可以对返回值使用命名返回值方式直接进行设置。
参考链接:https://www.lmlphp.com/user/10603/article/item/422458
更多相关Go语言的技术文章或视频教程,请关注本公众号获取并查看,感谢你的支持与信任!
文章评论