go 错误处理

Go 语言的错误处理有两种方式:panic和error。

panic适用于严重的,调用方不可预料的错误,比如数组越界,即使告知调用方错误的原因,调用方也不可能在原地恢复程序正常流程。

error就是一般的错误,比如网络中断,调用方可以预料这种错误的发生,并能根据错误做出相应的处理。

有一种错误处理的模式是将panic通过recover转化成error,这种模式适用于内部错误多且杂,并且发生在各个函数调用中,但是对外只需要提供几种固定类型的错误即可。比如web server,内部错误或者是panic最终都要转化为4XX,5XX这样的应答。

代码示例:

func server(workChan <-chan *Work) {
    for work := range workChan {
        go safelyDo(work)
    }
}

func safelyDo(work *Work) {
    defer func() {
        if err := recover(); err != nil {
            log.Println("work failed:", err)
        }
    }()
    do(work)
}

有一点需要注意:panic 之后只执行本routine中的语句。所以下边的代码并不能阻止程序的Crash。

func main() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("recover outer: %s", err)
		}
	}()

	go func() {
		panic("panic in new routine")
	}()

	time.Sleep(2 * time.Second)
	fmt.Println("go to here?")
}

更多的时候程序通过返回error来传递错误,但是error是一个接口类型,只能传递一个字符串出来。这对于调用者判断错误类型和更详细的调查错误原因是远远不够的。

简单一点的模式是设置全局的导出error变量(因为go中没有error类型的常量),但是这个变量的值永远不要变(正常的使用也没有变的必要),然后在接口中返回对应错误值,调用方使用==直接比较返回值和导出的error变量。

package search

var (
	MyErrNotExist     = errors.New("资源不存在")
)

func Find(name string) error {
	return MyErrNotExist
}

search.Find函数可能还返回其他错误,调用方只要比较返回值是不是search.MyErrNotExist就可以判断错误类型。

还有一种方式是包提供err类型的判断方式,go语言的os包就是这么做的:

package main

import (
	"fmt"
	"os"
)

func main() {
	filename := "a-nonexistent-file"
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		fmt.Println("file does not exist")
	}
}

对于大规模系统,仅仅返回error类型可能是不够的,还应该告知error发生在那个函数中,最好还有一个ID,能顺着ID找到一系列相关的上下文。《Concurrency in Go 》中给出一种自定义error实现:

type MyError struct{
    Inner 		error
    Message 	string
    StackTrace	string
    Misc		map[string]interface{}
}

func wrapError(err error, message string, msgArgs ...interface{}) MyError{
    ...
    return
}

func (err MyError)Error()string{
    return err.Message
}

首先MyError是一个error,这样保证了返回数据类型的兼容性;其次,Inner是一个error,能保存每一层调用的原始错误信息;StackTrace给出函数调用堆栈;Misc可以存放额外的自定义数据,比如requestID。这样当调用方收到一个error的时候,可以根据自己的需要决定是否深挖错误原因,还是封装错误给上层。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值