本文来源于《The Go Programming Language》

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被不加区分的恢复,可能会导致漏洞被忽略。

虽然把对panic的处理都集中在一个包下,有助于简化对复杂和不可以预料问题的处理,但作为被广泛遵守的规范,你不应该试图去恢复其他包引起的panic。公有的API应该将函数的运行失败作为error返回,而不是panic。同样的,你也不应该恢复一个由他人开发的函数引起的panic,比如说调用者传入的回调函数,因为你无法确保这样做是安全的。

有时我们很难完全遵循规范,举个例子,net/http包中提供了一个web服务器,将收到的请求分发给用户提供的处理函数。很显然,我们不能因为某个处理函数引发的panic异常,杀掉整个进程;web服务器遇到处理函数导致的panic时会调用recover,输出堆栈信息,继续运行。这样的做法在实践中很便捷,但也会引起资源泄漏,或是因为recover操作,导致其他问题。

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

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

// soleTitle returns the text of the first non-empty title element// in doc, and an error if there was not exactly one.func soleTitle(doc *html.Node) (title string, err error) {type bailout struct{}defer func() {switch p := recover(); p {case nil:       // no paniccase bailout{}: // "expected" panic            err = fmt.Errorf("multiple title elements")default:panic(p) // unexpected panic; carry on panicking        }    }()// Bail out of recursion if we find more than one nonempty title.    forEachNode(doc, func(n *html.Node) {if n.Type == html.ElementNode && n.Data == "title" &&            n.FirstChild != nil {if title != "" {panic(bailout{}) // multiple titleelements            }            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在运行时终止程序,如内存不足。

- End -

好文点赞收藏

捕获异常_Recover捕获异常相关推荐

  1. pyqt5 捕获异常确保程序不退出_Python异常处理详解(基础篇十一)

    一 异常 1 什么是异常? 示例: 说明: 打开一个不存在的文件123.txt,当找不到123.txt 文件时,就会抛出给我们一个IOError类型的错误,No such file or direct ...

  2. java代码走查常见错误_FindBugs常见错误描述和解决方法

    (一)[DLS_DEAD_LOCAL_STORE] 描述: Dead store to 未使用的局部变量 解决方法:局部变量定义后未使用:实例化对象后又重新对该对象赋值 (二) [ST_WRITE_T ...

  3. 半吊子菜鸟学Web开发 -- PHP学习 4 --异常

    PHP异常处理 1 抛出一个异常 与Python的try except类似,PHP用try catch来捕获异常 基本语法 try{//可能出现错误或异常的代码//catch表示捕获,Exceptio ...

  4. Java的知识点19——异常机制Exception

    异常(Exception)的概念 异常指程序运行过程中出现的非正常现象,例如用户输入错误.除数为零.需要处理的文件不存在.数组下标越界等. Java是采用面向对象的方式来处理异常的.处理过程: 1.  ...

  5. python入门程序异常_Python入门基础(10)_异常_1

    最近有点忙,到现在快一个月没写了,罪过罪过,继续学习 异常:python程序在运行时,如果python解释器遇到一个错误,那么程序就会停止执行,并且会提示一些错误信息,这就是异常. 抛出异常:程序停止 ...

  6. JSTracker:前端异常数据采集

    JSTracker - 淘宝前端监控平台 基本上服务器端的代码都是处于 7x24 小时的实时监控状态的,一旦有任何异常对应的开发同学就马上收到报警,并且第一时间处理. 但是对于前端来说,往往是实际用户 ...

  7. C++ 异常的详细介绍

    C++ 异常的详解 程序有时会遇到运行阶段错误,导致程序无法正常执行下去.c++异常为处理这种情况提供了一种功能强大的而灵活的工具.异常是相对比较新的C++功能,有些老编译器可能没有实现.另外,有些编 ...

  8. 运行时异常与一般异常有何异同_Java修行第015天,异常机制和常用类

    1. 异常概念_分类 1) 什么叫异常? 答:异常(Expection)就是在程序运行过程中所发生的不正常的事件,它会中断正在运行的程序 2) 请简述异常的分类? 答:异常分为Error(仅靠程序本身 ...

  9. Java笔记11-【异常、线程】

    主要内容 异常.线程 第一章 异常 1.1 异常概念 异常,就是不正常的意思.在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是: 异常 :指 ...

最新文章

  1. puppeteer爬虫的奇妙之旅
  2. 利用php打印出九行菱形,PHP新手上路(九)
  3. Oracle 的基本特点,并完整描述安装过程
  4. phpAdmin数据库管理套件
  5. python中main()函数写法
  6. linux 开启新端口号,linux 查看端口,开启新端口
  7. 微软Cloud+AI本地化社区贡献指南
  8. 街机linux有安卓好吗,Ubuntu下用xmame玩街机游戏
  9. Scala基础之变量和数据类型
  10. CCF CSP202006-1 线性分类器
  11. golang redis 队列删除图片
  12. sqlmap安装及问题汇总
  13. Linux文件I/O实验报告
  14. 圣思园——Java SE Lesson 4
  15. python 测试框架nose (nosetests)
  16. 前端设计-css网格布局的最佳实践
  17. 网络连通性以及网络不通解决办法
  18. 一周新论文 | 2020年第12周 | 自然语言处理相关
  19. ProM安装以及配置
  20. PHP获取ip及归属地信息

热门文章

  1. 论文浅尝 | 六篇2020年知识图谱预训练论文综述
  2. 论文笔记(Social Attentional Memory Network:Modeling Aspect- and Friend-level Differences in Recom-)
  3. 论文学习19-Structured prediction models for RNN based sequence labeling in clinical text(LSTM_CRF,2016)
  4. Aligning Plots in a Column作图列对齐
  5. 第二阶段团队冲刺(十)
  6. SpringBoot项目中,Redis的初次使用
  7. mybatis自学笔记-1
  8. hihocoder1457
  9. UVAL - 6755 - Swyper Keyboard
  10. SharePoint 2010中的内容类型集线器 - 内容类型发布与订阅