高级Go Panic编程

像专业人士一样惊慌
我这样做是因为我需要…

先决条件

本文希望你已经非常熟悉golangpanicrecover功能,并与具有任何其他编程语言exceptionstry-catch )的概念。

介绍

您可能已经在“ The Little Go Book ”中看到了类似以下内容:

Go处理错误的首选方法是通过返回值,而不是异常

也许您在go wiki上看到了“ CodeReviewComments ”页面,其中说:

不要对常规错误处理使用恐慌。 使用错误和多个返回值

另外,您可能已经看过“ Effective Go ”一文,其中说:

向调用者报告错误的通常方法是将错误作为额外的返回值返回

此外,您可能已经在Dave Cheney的博客文章“ Why Go如何正确处理异常 ”中看到,该文章说:

当您在Go中惊慌失措时,您会感到恐惧,这不是别人的问题,而是人类的游戏

感觉恐慌是最好在自己的项目中避免的事情……。
但这是否意味着没有人恐慌?
让我们来看看! 让我们对流行的go项目运行下一个命令,看看是否没有人使用过恐慌:

grep "panic(" -r --include=*.go . | wc -l

结果:

+-------------+-----------------+
| name | count of panics |
+-------------+-----------------+
| go | 4050 |
| kubernetes | 4087 |
| gin | 46 |
| prometheus | 693 |
| terraform | 1161 |
| echo | 14 |
| dep | 157 |
| gorilla mux | 9 |
| mysql | 5 |
| pq | 46 |
+-------------+-----------------+

好…

如何生活

乍一看,文档,书籍和文章说不使用恐慌可能令人感到困惑,但现实是另一种情况,我们到处都能看到恐慌……

希望您会同意“恐慌不是简单地说“使用或不使用”的想法。

因此,让我们尝试更深入地研究并阐明恐慌的明暗两面之间的界限,以及为什么我们在github上有这么多的恐慌以及为什么所有书籍和文档都不喜欢恐慌。

什么是恐慌

官方文件说:

panic内置函数停止当前goroutine的正常执行

PanicAndRecover ”维基页面说:

应急和恢复功能的行为类似于异常,并在某些其他语言中进行try / catch

而且“ 以身作则 ”还说:

恐慌通常表示发生了意外错误。 通常,我们使用它来快速解决正常操作期间不应该发生的错误

好吧……现在感觉到恐慌就像其他语言中的例外一样,它可以解释前面提到的github项目中的大量恐慌。

但是,如果您看过戴夫·切尼(Dave Cheney)的博客文章“ 为什么Go可以正确处理异常 ”,您可能会看到以下内容:

您可能会想到恐慌与投掷相同,但是您错了

这意味着恐慌与其他语言中的throw exception没有什么不同,并且有其自身的优缺点。

优点

  1. 就像其他语言中的throw exception一样,它会停止程序执行并将堆栈展开到顶级函数调用。
  2. 无需处理多个返回值,也无需编写无聊的检查: if err != nil { // handle error }
    结果-代码更易于阅读。

缺点

  1. 万一您无法recover将会停止程序。
  2. go执行展开堆栈时,它将收集有关整个调用堆栈的信息,这可能很慢。
  3. 函数recover返回interface{} ,您必须对获得的值执行类型检查,该值可能很慢(尤其是在反射的情况下)。 这不像在其他语言中catch某些exception一样。
  4. goroutine恐慌的情况下,函数recover不会停止恐慌。 而且它也不是其他语言中try-catch传统方式。

何时使用恐慌

现在很明显,恐慌是一种尖锐的工具,您在使用它之前必须三思。 并且介绍了简介部分中提供的所有注意事项。

另外“ Effective Go ”说:

一个可能的反例是在初始化期间:如果库确实无法设置自身,则恐慌是合理的

而且,如果由于某些情况而无法继续执行时,您可能会因恐慌而停止程序。

使用恐慌的另外一个原因

我相信,如果您要构建具有复杂业务逻辑和分层体系结构(而且具有支持域驱动的设计)的覆盖范围应用程序,则必须使用恐慌方法。
您可能会讨厌我,但我相信这是不淹没错误处理并拥有清晰业务逻辑的唯一方法。

到处都是恐慌

首先,简介部分提供的数字意味着我们总是必须处理恐慌(即使我们没有在代码中明确使用恐慌)
因为下游的某些事情甚至可能使语言本身发生恐慌,并且为了避免程序停止,我们不得不使用恐慌处理程序( recover )。
如果项目具有用户界面(从用户/其他服务获取命令/请求并提供结果/响应),这也非常重要,因为即使在未处理的严重错误的情况下,我们始终必须以确定的消息格式提供结果/响应。
因此,在main.go我们必须具有以下内容:

func main() {
defer func() {
if r := recover(); r != nil {
// handle panic
}
}()
// ...
}

这只是简单的示例,但您可以在此处阅读有关defer-recover更多信息。

同样重要的是要承认,如果您要启动新的goroutine则必须进行defer-recover ,否则您将无法处理goroutine恐慌。
您可以在“处理错误和恐慌”一章的“ 实践中 ”中阅读有关此内容的更多信息,在这里,我将提供最有趣的图片:

像专业人士一样惊慌
正常流量。
像专业人士一样惊慌
恐慌。

语法糖

一旦开始更频繁地使用恐慌,您还必须更频繁地执行recover并且以一种令人愉快的方式进行recover ,可以使用软件包恢复之类的方法 该程序包的主要思想是简化恐慌恢复,并提供以其他方式执行恢复的机会:

您可能会发现此语法与其他语言中catch exception的传统方式非常相似,但是此方法的主要目标是简单明了,而且易于阅读,理解和预测此代码块的行为。

比较方式

让我们比较两种方法:1 —返回错误,2 —恐慌。
为了进行比较,让我们使用一个简单的示例:假设我们有:
1) facade -服务这产生对Facebook,Twitter和Pinterest的用户。
2)调用外观服务的controller ,检查错误并打印结果。
序列图如下所示:

像专业人士一样惊慌

实施#1

在这里,您可以看到控制器中的超级简单功能SignUp ,该service.SignUp调用service.SignUp然后检查服务中的错误并打印结果(一切清晰,简单明了)。

众所周知,此代码是惯用的,可以处理go错误。 太好了!

但是,在服务方面-在这里您可以找到很多重复的代码,感觉好像出了问题…

(您可以在此处找到源代码)。

实现#2

在这里,您可以在控制器中看到相同的SignUp函数,该函数调用service.MustSignUp然后执行recover (通过恢复包)并打印结果(相同流程)。
而且,如果您打算提供服务,则可能会发现它看起来更短,更简单,并且更容易阅读和理解这种业务逻辑。

但是这个代码是已知的,被禁止go ,由于先前指定的原因...

(您可以在此处找到源代码)。

真的不好吗

从技术上讲,这两个实现相等,并提供相同的功能,相同的错误和相同的结果(您可以在此处查看 )。
但是关于代码量-很明显,第二个代码更简单,您可以在下图看到它:

像专业人士一样惊慌

同样,第一个实现没有recover但是它应该recover ,因为每个用户友好的项目都必须有恢复,这意味着第一个实现将包含更多代码。

慢吗

在如此小的示例上执行基准测试可能看起来很愚蠢,但是无论如何,让我们看看它的外观并弄清楚我们是否偏斜了数字:

+---------------------------------+----------+----------+
| case | imp. #1 | imp. #2 |
+---------------------------------+----------+----------+
| error: username cannot be blank | 53000 ns | 45000 ns |
| error: username already taken | 51000 ns | 46000 ns |
| ok | 32000 ns | 34000 ns |
+---------------------------------+----------+----------+

看起来好像在发生错误的情况下–紧急情况更快,但在成功的情况下– recover需要一些开销……
请注意,所有提供的数字均以纳秒为单位,
这意味着:对于这种特殊情况,我们在两种方法之间并没有太大的区别……

(您可以在此处找到与此基准测试相关的源代码)。

进行2次草稿

您可能已经知道, go 2错误处理将通过check-handle组合得到改善(如果没有,请看一看 ),它将以非常优雅的方式简化一切!
但这是否有助于构建复杂的分层应用程序?
对于像我们这种情况(控制器服务)这样的非常简单的应用程序,答案是肯定的,但是不幸的是,对于大型应用程序,尤其是对于具有支持域驱动的设计check-handle应用程序而言,这是没有用的,
我相信您仍将不得不使用恐慌...

结论

本文的重点是表明恐慌只是一种工具,您不必害怕此工具,您必须知道何时以及如何使用恐慌……
而且,一旦您知道此工具的优缺点,就可以利用或不使用它。

聚苯乙烯

您可以在此处找到具有分层体系结构(不是DDD,而是多层)的演示项目,该体系结构到处都有恐慌的想法,也许是说明性的。

此外,您还可以在此处找到同时使用两种方法errors vs panic更多示例。

如果您不喜欢恐慌,则可以找到另一种方法 ,以另一种方式简化错误处理。

From: https://hackernoon.com/panic-like-a-pro-89044d5a2d35

相关文章:

  • 2021-12-21
  • 2021-06-05
  • 2021-10-15
  • 2021-11-09
  • 2021-07-29
  • 2021-10-13
  • 2021-11-02
猜你喜欢
  • 2021-12-31
  • 2021-12-17
  • 2021-11-27
  • 2021-11-30
  • 2021-04-22
  • 2021-12-11
  • 2022-12-23
相关资源
相似解决方案