1. 在默认情况下,当发生错误(panic)后,程序就会终止运行
  2. 如果发生错误后,可以捕获错误,并通知管理人员(邮件或者短信),程序还可以继续运行,这当然无可厚非
  3. errors.New("错误信息"),会返回一个error类型的值,表示一个错误
  4. panic内置函数,接收一个interface()类型的值(也就是任何值都可以)作为参数,可以接收error类型的变量,输出错误信息,并退出程序
  5. GO语言追求简洁优雅,GO语言不类似php支出try catch操作
  6. GO语言中引入的处理方式为:defer、panic、recover
  7. GO可以抛出一个panic异常。然后在defer中通过recover捕获异常再处理

自定义错误处理运用

package main
import (
    "fmt"
    "errors"
)


//此函数读取配置文件信息
//如果文件名不正确,返回自定义错误
func readConfFile(FileName string) (err error) { //返回error类型
    if FileName == "config.ini" {
        return nil //表示没有错误
    } else {
        return errors.New("读取文件错误")
    }
}

func error_func(){
    err := readConfFile("config.ini")  //这里故意写错,报错代码在第一块,如果写对,在第二块
    if err != nil {
        //如果读取文件发生错误,就输出这个错误,并终止程序
        panic(err)  //这个函数作用是打印错误信息,并终止程序
    }
    fmt.Println("error_func()继续执行")
}
func main() {
    error_func()
    fmt.Printf("发生错误后面的代码")
}



//【报错信息】
//一、
// panic: 读取文件错误

// goroutine 1 [running]:
// main.error_func()
//         D:/goproject/src/main/hello.go:22 +0xdf
// main.main()
//         D:/goproject/src/main/hello.go:27 +0x27
// exit status 2


//二、
// error_func()继续执行
// 发生错误后面的代码

recover+defer的运用

package main
import (
    "fmt"
    "time"
)



//这里举例,在数学计算中0是不可以作为被除数的
func error_func() {
    //这里使用defer + recover来捕获处理异常
    defer func() {  //defer就是把匿名函数压入到defer栈中,等到执行完毕后或者发生异常后调用匿名函数
        err := recover()  //recover是内置函数,可以捕获到异常
        if err != nil {   //说明有错误
            fmt.Println("err=", err)
            //当然这里可以把错误的详细位置发送给开发人员
            //send email to admin
        }
    }()
    num1 := 10
    num2 := 0
    res := num1 / num2
    fmt.Println("res=", res)
}

func main() {
    //这样程序不会轻易挂掉
    error_func()
    i := 0
    for {
        i++
        fmt.Println("发生错误后面的代码", i)
        time.Sleep(time.Second)
    }

}

defer语句是在return之后执行的,例如:

func test() (result int) {
    defer func() {
        result = 12
    }()
    return 10
}

func main() {
    fmt.Println(test())     // 12
}

在一个函数中panic被调用后,其defer语句仍会执行,例如:

package main
import (
    "fmt"
    "errors"
)


func foo()(n int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("再然后④\n")
            n++   
            fmt.Println("最后⑤\n")
        }
    }()
    fmt.Println("首先①\n")
    n++       
    fmt.Printf("然后②n=%v\n" ,n) 
    fmt.Println("然后③\n")     
    panic(errors.New("i'm a bug"))  
    fmt.Println("没走\n")
            
    return n
}
func main() {
    n := foo()
    fmt.Printf("n最后的值%v", n)
}
首先①
然后②n=1
然后③
再然后④
最后⑤
n最后的值2

 

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

通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。

提示

在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch 机制捕获异常,没有被捕获的严重异常会导致宕机,捕获的异常可以被忽略,让代码继续运行。
Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制

 

panic 和 recover 的关系

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

  • 有 panic 没 recover,程序宕机。
  • 有 panic 也有 recover,程序不会宕机,执行完对应的 defer 后,从宕机点退出当前函数后继续执行。

提示

虽然 panic/recover 能模拟其他语言的异常机制,但并不建议在编写普通函数时也经常性使用这种特性。

在 panic 触发的 defer 函数内,可以继续调用 panic,进一步将错误外抛,直到程序整体崩溃。

如果想在捕获错误时设置当前函数的返回值,可以对返回值使用命名返回值方式直接进行设置。