Go语言圣经 - 第5章 函数 - 5.10 Recover捕获异常

第5章 函数

函数可以让我们将一个语句序列打包成一个单元,然后可以从程序中其他地方多次调用,函数的机制可以让我们把一个大的工作分解成小任务。前面我们已经接触过函数,本章我们将讨论函数的更多特性

5.10 Recover捕获异常

通常来说,我们不应该对panic异常做任何处理,但是有时候可以恢复。例如:Web服务器遇到不可预料的严重问题时,奔溃前应该关闭所有链接,如果不做处理,那客户端一直会处于等待状态。如果Web服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮调试

如果在deferred函数中调用了内置函数recover,并且定义了该defer语句的函数发生了panic异常,recover会使程序从panic中恢复,并返回panic value,导致panic异常的函数不会继续运行,但是可以正常返回,在未发生panic时调用recover,recover会返回nil

让我们以解析器为例,说明recover的使用场景。考虑到语言解析器的复杂性,即使某个语言解析器目前工作正常,也无法肯定它没有漏洞。 因此,当某个异常出现时,我们不会选择让解析器奔溃,而是会将Panic异常当作普通的解析错误,并附加额外信息提醒用户报告此错误

func Parse(input string)(s *Syntax,err error) {
   defer func() {
      if p := recover();p != nil {
         err = fmt.Errorf("internal error :%v",p)
      }
   }()
   //...Parser...
}

deferred函数帮助Parse从Panic中恢复。在deferred函数内部,Panic value被附加到错误信息中;并用err变量接收错误信息,返回给调用者,我们也可以通过调用runtime.Stack往错误信息中添加完整的堆栈调用信息

不加区分的回复所有Panic异常,并不可取;因为在panic之后,我们无法保证包级变量的状态仍然和我们预期一致。比如,对数据结构的一次重要更新没有被完成、文件或者网络连接没有关闭、获得的锁么有释放。此外,如果写日志时产生的panic被不加区分的恢复,可能会导致漏洞被忽略

有时,我们很难完全遵循规范,举个例子,net/http包中提供了一个Web服务器,将收到的请求分发给用户提供的处理函数,很显然,我们不能因为某个处理函数引发的panic异常,就杀掉整个进程;Web服务器遇到处理函数导致的panic时会调用recover,输出堆栈信息,继续运行

这样的做法在实践中很便捷,但也会引起资源泄漏,或是因为recover操作,导致其他问题

基于以上原因,安全的做法是有选择是的recover。也就是说,只恢复应该被恢复的panic异常。此外,这些异常所占的比例应尽可能的低。为了标识某个panic是否应该被恢复,我们可以将panic value设置成特殊类型,在recover 时堆panic value进行检查,如果发现它是特殊类型,就将这个panic作为error处理,如果不是,则按照正常的panic处理。下面的例子中,我们会看到这种方式:

下例是title函数的变形,如果HTML页面包含多个,该函数会给调用者返回一个错误(error),在soleTitle内部处理时,如果检测到多个<title>,会调用panic,组织函数继续递归,并将特殊类型bailout作为panic的参数

forEachNode (doc , func(n *html.Node)) {
   if n.Type == html.ElementNode && n.Data == "title" && n.FirstChild != nil {
      if title != "" {
         panic(bailout{})
      }
      title = n.FirstChild.Data
   }, nil)
   if title == "" {
      return "",fmt.Errorf("no title element")
   }
   return title,nil
}

在上例中,deferred函数调用recover,并检查panic value,当panic value是bailout{}类型时,deferred函数生成一个error返回给调用者。当panic value是其他non-nil值时,表示发生了未知的panic异常,deferred函数将调用panic函数,并将当前的panic value 作为参数传入;此时,等同于recover 没有做任何操作(请注意:在例子中,对可预期的错误采用了panic,这违反了之前的建议,我们在此只是想向读者演示这种机制)

有些情况下,我们无法恢复。某些致命错误会导致Go在运行时终止程序,如内存不足

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值