宕机与恢复

* 本页面主要介绍Go语言宕机与恢复的相关内容。

一、宕机(Panic)—— 程序终止运行

我们知道GO语言是编译型语言,在代码编译阶段会捕获很多错误,但是有些错误只能在运行时检查,如数组访问越界、空指针引用等,这些运行时错误会引起宕机,也就是我们经常提到的Panic。

通过学习我们在 函数function 一节我们总结的内容,我们知道:一般而言,当宕机发生时,程序会中断运行,并立即执行在该 协程goroutine (第6章我们将讲到,可以先理解成线程)中被延迟的函数(defer 机制),随后,程序崩溃并输出日志信息,日志信息包括 panic value 和函数调用的堆栈跟踪信息,panic value 通常是某种错误信息。

因为panic在GO语言中属于严重错误,会导致程序终止运行,所以我们需要消除一切可能的panic。

panic除了出现故障时被触发外,我们还可以手动触发宕机,让程序崩溃,这样开发者可以及时地发现错误,同时减少可能的损失。

  func main() {
      //内建函数panic,参数可以是任意类型的
      panic("crash")
  }
 
  //输出
  panic: crash

  goroutine 1 [running]:
  main.main()
      D:/code/main.go:4 +0x40
  exit status 2
二、延迟执行(Defer)—— 做一些“补救”措施

具体内容可以参考 延迟调用 这一节的内容。

三、宕机恢复(Recover)—— 防止程序崩溃

Recover 是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,recover 仅在延迟函数 defer 中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果,如果当前的 goroutine 陷入恐慌,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行。

在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch 机制捕获异常,没有被捕获的严重异常会导致宕机,捕获的异常可以被忽略,让代码继续运行。

Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制。

下面的代码实现了 ProtectRun() 函数,该函数传入一个匿名函数或闭包后的执行函数,当传入函数以任何形式发生 panic 崩溃后,可以将崩溃发生的错误打印出来,同时允许后面的代码继续运行,不会造成整个进程的崩溃。

  package main

  import (
      "fmt"
      "runtime"
  )

  // 崩溃时需要传递的上下文信息
  type panicContext struct {
      function string // 所在函数
  }

  // 保护方式允许一个函数
  func ProtectRun(entry func()) {

      // 延迟处理的函数
      defer func() {

          // 发生宕机时,获取panic传递的上下文并打印
          err := recover()

          switch err.(type) {
          case runtime.Error: // 运行时错误
              fmt.Println("runtime error:", err)
          default: // 非运行时错误
              fmt.Println("error:", err)
          }

      }()

      entry()

  }

  func main() {
      fmt.Println("运行前")

      // 允许一段手动触发的错误
      ProtectRun(func() {

          fmt.Println("手动宕机前")

          // 使用panic传递上下文
          panic(&panicContext{
              "手动触发panic",
          })

          fmt.Println("手动宕机后")
      })

      // 故意造成空指针访问错误
      ProtectRun(func() {

          fmt.Println("赋值宕机前")

          var a *int
          *a = 1

          fmt.Println("赋值宕机后")
      })

      fmt.Println("运行后")
  }

  //运行结果

  //运行前
  //手动宕机前
  //error: &{手动触发panic}
  //赋值宕机前
  //runtime error: runtime error: invalid memory address or nil pointer dereference
  //运行后

panic 和 recover 的组合有如下特性:

  • 《Go程序设计语言》
  • 有 panic 没 recover,程序宕机
  • 有 panic 也有 recover,程序不会宕机,执行完对应的 defer 后,从宕机点退出当前函数后继续执行

这部分内容就总结到这里,Get了不?


* 本页内容参考以下数据源:

  • 《Go程序设计语言》
  • http://c.biancheng.net/view/63.html
  • http://c.biancheng.net/view/64.html

凯冰科技 · 代码改变世界,技术改变生活
Next Page→