Go函数篇--错误处理和异常处理

错误处理和异常处理

  • 错误处理使用error
  • go中没有结构化的异常处理,通过panic抛出,recover捕获

一、error和panic recover以及os.Exit区别

1.1.error

error是一个内置的接口

1
2
3
4
5
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}

他只有一个Error方法,只要实现了Error,就是实现了error

1.2.panic和recover

panic是一个内置函数接收一个interfact,recover是一个内置函数,返回一个interface

1
2
func panic(v interface{})
func recover() interface{}
  • panic用于不可恢复的错误
  • panic退出前会执行defer指定的内容

要注意不能随便recover,有可能是系统出错了,而在程序里面使用recover,只是记录了一下,没有做其他处理,可能会导致health check失效,形成僵尸服务进程

对于不确定性的错误,比较好的方式是直接让我们的程序crash,让其托管的守护进程(比如systemd)帮他重启。

1.3.os.Exit

  • os.Exit退出的时候,不会调用defer指定的函数
  • os.Exit退出时,不会输出当前调用栈信息

二、错误处理

2.1.简单使用

  • 如果只有一种错误情况,一般用bool类型来说明函数是否执行成功,如果有多种错误情况,就用error类型

    1
    2
    3
    4
    5
    _, ok := Func()
    if !ok {
    // 错误处理
    }

  • 一般通过函数的返回值来进行错误判断,err != nil的时候,处理错误

1
2
3
4
5
6
7
8
func main() {
conent, err:=ioutil.ReadFile("filepath")
if err !=nil{
//错误处理
}else {
fmt.Println(string(conent))
}
}

2.2.自定义error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type fileError struct {
errString string
}

func (fe *fileError) Error() string {
return fe.errString
}

func openFile() ([]byte, error) {
return nil, &fileError{"文件打开错误"}
}

func main() {
conent, err := openFile()
if err != nil {
fmt.Println(err)
} else {
fmt.Println(string(conent))
}
}

2.3.函数错误处理策略

  1. 将错误继续传递下去
  2. 内部出现错误,可以进行重试,如果多次重试无果,再返回错误;例如设置一个HTTP请求的timeout重试n次
  3. 如果重试不能解决,应该返回错误信息,然后调用者进行处理
  4. 有些时候,可以忽略错误,记录一下,继续执行
  5. 有些时候,直接忽略处理,继续执行,这种情况比较少。(比如创建一个目录前判断目录有没有存在)

三、宕机和恢复

panic是程序异常(宕机)了,recover则可以用来将程序从panic中恢复继续执行

3.1.简单使用

  • recover只能捕获上一个panic
  • defer调用中引发的panic,可被后续defer调用捕获,仅最后一个错误可被捕获
  • 多个panic 要多个recover去捕获, recover写在panic前面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

func test1() {
// 捕获panic in test defer 1
defer func() {
if err := recover(); err != nil{
fmt.Println(err)
}
}()

defer func() {
panic("panic in test defer 1")
}()

defer func() {
panic("panic in test defer 2")
}()

// 捕获panic in test
defer func() {
if err := recover(); err != nil{
fmt.Println(err)
}
}()

panic("panic in test")
}

func main() {
defer func(){
if err := recover(); err != nil{
fmt.Println(err)
}
}()
test1()
println("before panic in main")
panic("panic in main")
}
  • 直接在defer中调用recover才能捕获到panic
1
2
3
4
5
6
7
8
9
10
11
12
13
func test2() {
defer func() {
fmt.Println("defer函数中直接调用recover才能捕获到", recover())
}()
defer fmt.Println("defer直接调用fmt.Println,recover捕获不到,返回nil", recover())
defer func() {
func() {
fmt.Println("defer函数中的函数执行recover,捕获不到,返回nil", recover())
}()
}()

panic("test panic")
}

Refs