golang – 利用recover捕获panic异常

在我们开发golang程序时,很难保证研发同学不会写出”访问空指针”或者”除0″之类的错误代码逻辑。

当golang运行时执行到bug代码的时候,会抛出panic异常。

如果我们不主动进行捕获,那么异常就会沿着调用栈逐层向上传播,直到最终导致程序崩溃退出。

假设我正在开发一个web框架,那么就需要在框架层面做顶层的panic异常捕获,避免单个请求处理过程中的bug导致程序整体崩溃,做到请求之间互不影响是我们的目标。

未捕获panic的情况

下面演示一个”除0错误”,可以看到panic异常抛出并且导致程序崩溃:

程序输出:

golang默认会把崩溃的堆栈输出到屏幕上。

一个健壮的web框架不应该因为某一个接口的逻辑不严谨而导致程序崩溃,因此必须对其进行异常捕获。

利用recover捕获panic异常

因为stupidCode内部会抛出panic异常沿着调用栈向上传播,stupidCode的上一层就是main。

为了在main方法中捕获到stupidCode抛来的panic异常,我们必须利用defer进行捕获,它将在离开main方法前被执行。

在defer中调用recover可以抓住panic异常,这样panic异常就停止了继续向上传播,程序便不会崩溃。

程序输出如下:

即recover成功抓到了panic并转换成了一个err。

错误的recover用法

一旦panic错误抛出,则golang不会继续执行后续代码,而是立即离开当前函数,因此只有利用defer才能确保recover捕获的执行。

下面是一个错误的recover用法,其并不会被执行,因此也无法捕获到panic:

代码输出:

打印出panic调用栈

我们在recover抓到异常后,需要像golang默认的那样打印除调用栈,以便分析问题。

这里调用runtime.Caller即可获取每一层调用栈,数字0表示当前层级,也就是runtime.Caller(0)这一行调用。

其输出如下:

调用栈的第一行是最近的一个调用,也就是defer函数这一层栈。

到达defer函数前有2层golang内部的panic的调用栈,然后就是抛出异常的调用栈了:

17404496 /Users/liangdong/go/src/blog/demo1/main.go 10

fmt.Println(1 / n)这行代码引起的。

所以,实际上我们输出调用栈可以直接跳过前3层,它们都是因为panic与捕获panic而引入的栈,对分析问题没有意义:

这样的调用栈就没有多余信息了:

 本节完。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~