Go - panic
原则
- 不要相信任何
外部输入的参数- 函数需要对所有输入的参数进行
合法性的检查 - 一旦发现问题,
立即终止函数的执行,返回预设的错误值
- 函数需要对所有输入的参数进行
- 不要忽略任何一个
错误显式检查这些函数调用返回的错误值- 一旦发现错误,要及时终止函数执行,防止错误继续传播
- 不要假定
异常不会发生异常不是错误- 错误是
可预期的,也会经常发生,有对应的公开错误码和错误处理方案 - 异常是
不可预期的,通常指的是硬件异常、操作系统异常、语言运行时异常、代码 Bug(数组越界访问)等
- 错误是
- 异常是
小概率事件,但不能假定异常不会发生- 根据函数的角色和使用场景,考虑是否要在函数内设置
异常捕获和恢复的环节
- 根据函数的角色和使用场景,考虑是否要在函数内设置
panic
在 Go 中,由
panic来表达异常的概念
- panic 指的是 Go 程序在
运行时出现的一个异常情况- 如果异常出现了,但没有被
捕获并恢复,则 Go 程序的执行会被终止 - 即便出现异常的位置不在
主 goroutine
- 如果异常出现了,但没有被
- panic 来源:
Go 运行时/ 开发者通过panic函数主动触发 - 当 panic 被触发,后续的执行过程称为
panicking
手动调用
panic函数,主动触发panicking
- 当函数 F 调用 panic 函数,函数 F 的执行将
停止 - 函数 F 中已进行求值的
deferred函数都会得到正常执行 - 执行完所有
deferred函数后,函数 F 才会把控制权返回给其调用者 - 函数 F 的调用者
- 函数 F 之后的行为就如同调用者
自己调用 panic 函数一样 -递归
- 函数 F 之后的行为就如同调用者
- 该
panicking过程将继续在栈上进行,直到当前 goroutine中的所有函数都返回为止 - 最后 Go 程序
崩溃退出
1 | package main |
recover
recover 是 Go 内置的专门用于
恢复 panic的函数,必须被放在一个defer函数中才能生效
1 | package main |
defer函数类似于function close hook
每个函数都 defer + recover:
心智负担+性能开销
经验
评估程序对 panic 的
忍受度,对于关键系统,需要在特定位置捕获并恢复panic,以保证服务整体的健壮性
- Go http server,每个
客户端连接都使用一个单独的 goroutine进行处理的并发处理模型 - 客户端一旦与 http server 连接成功,http server 就会为这个连接新建一个 goroutine
- 并在该 goroutine 中执行对应连接的 serve 方法,来处理这条连接上客户端请求
- panic 危害
- 无论哪个 goroutine 中发生
未被恢复的 panic,整个 Go 程序都将崩溃退出
- 无论哪个 goroutine 中发生
- 需要保证某一客户端连接的 goroutine 出现 panic 时,不影响 http server
主 goroutine的运行- serve 方法在一开始就设置了 defer 匿名函数,在 defer 匿名函数中
捕获并恢复了可能出现的 panic
- serve 方法在一开始就设置了 defer 匿名函数,在 defer 匿名函数中
并发程序的异常处理策略:局部不影响整体
1 | // Serve a new connection. |
提示
潜在的 Bug- 触发了非预期的执行路径
在 Go标准库中,大多数panic的使用都是充当类似断言的作用
1 | // phasePanicMsg is used as a panic message when we end up with something that |
1 | func (w *reflectWithString) resolve() error { |
不要混淆
异常和错误
- 在 Java 标准类库中的
checked exception,类似于 Go 的哨兵错误值- 都是
预定义的,代表特定场景下的错误状态
- 都是
- Java checked exception 用于一些
可预见的,经常发生的错误场景- 针对 checked exception 所谓的异常处理,本质上是针对这些场景的
错误处理预案 - 即对 checked exception 的定义、使用、捕获等行为都是
有意而为之 - 必须要被
上层代码处理:捕获 or 重新抛给上层
- 针对 checked exception 所谓的异常处理,本质上是针对这些场景的
- 在 Go 中,通常会引入大量第三方包,而
无法确定这些第三方 API 包中是否会引发panic- API 的使用者不会逐一了解 API 是否会引发 panic,也没有义务去处理引发的 panic
- 一旦 API 的作者
将异常当成错误,但又不强制 API 使用者处理,会引入麻烦 - 因此,不要将
panic当成error返回给 API 的调用者,大部分应该返回error,即 Java checked exception
| Java | Go |
|---|---|
Checked Exception |
error |
RuntimeException / Error |
panic |
Java 发生 RuntimeException,JVM 只会停止对应的线程;而 Go 发生 panic,会整个程序崩溃
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.











