Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Golang Error创建和处理

Golang实践之Error创建和处理详解

作者:小雄Ya

在 C#、Java 等语言中常常使用 try...catch的方式来捕获异常,但是在Golang 对于错误处理有不同的方式,像网上也有很多对 error 处理的最佳实践的文章,其中很多其实就是对 error 的统一封装,使用规范进行约束,本文主要是记录自己对处理 Error 的一些认识和学习

error 基本概念

type error interface{
    Error() string
}

Golang 中 error类型是一个具有单个方法的接口,其内置方法是返回描述错误的字符串。Go 语言创始人觉得异常机制会导致控制流程混乱,降低代码的可读性,所以期望异常是一种可预知的。所以在使用时应当适当的返回 error ,然后调用方就可以更明确知道错误,进而再处理异常。同样也说明了开发人员在 coding 时要能识别和预测可能发生错误,再进行处理,而不是忽略错误,

panic 和 recover

但可能也会出现一些我们无法预料的错误情况。比较严重的程序错误导致发生 panic 就会使程序结束,所以它又提供一个 recover 来捕获 panic ,以便发生panic时能够重新执行启动程序。

defer func() {
        if r := recover(); r != nil {
            fmt.Println("重新启动程序:", r)
        }
}()

recover 函数最好在主函数中且必须在 defer 语句总使用,为了保证发生 panic 时被调用,如果没有panic时recover 函数将会返回 nil。

对于panic 的错误,应当全面记录错误信息,这样才有利于排查问题。对于一些比较重要的服务应当适当增加报警机制以通知相关团队或人员。

创建错误

// 方式1:使用 fmt.Errorof("")
func div(x, y int)(int,error){
    if y==0 {
        return 0,fmt.Errorof("除数 %d 不能为0",y)
    }
    return x/y, nil
}
// 方式2: 使用errors.New("")
ErrDivByZero := errors.New("除数不能为0")
return 0,ErrDivByZero
// 方式3:使用 errors.Wrap(err, "除数不能为0")
return 0,errors.Wrap(err, "除数不能为0")

方式1中 fmt.Errorf 函数允许使用格式化字符串创建新错误。

errors.Wrap 函数允许使用上下文包装error,使用场景:当函数调用另一个方法时遇到的错误而导致无法完成业务流程,那可能需要从函数返回错误。使用 errors.Wrap 函数需要注意:因其除了错误信息外还附加了堆栈信息,所以不能在程序中大量使用。

以上3种可以很简单方便的创建错误。当然支持自定义错误类型,通过创建实现 error 接口的新类型。例如:

// 方式4: 自定义错误类型
type MathCalError struct {
    message string
}
// 实现 Error 方法
func (e *MathCalError) Error() string {
    return e.message
}
func div(x, y int) (int, error) {
    if y == 0 {
        return 0, &MathCalError{"除数不能为 0"}
    }
    return x / y, nil
}

处理错误

根据遇到到场景,基本对于错误的处理大致有下面五种情况:

// 忽略错误
result,_ := getUser()
// 记录错误
result,err := getUser();err!=nil{
    log,writerInfo("something error", err.Error())
}
// 传递错误
result,err := getUser();err!=nil{
   return err
}
// 错误重试
retryCount := 0
 maxRetryCount := 3
for {
    result,err := getUser();
    if err!=nil{
       return err
       if retryCount >= maxRetryCount{
           return err
       } 
    }
    select {
    case <-ctx.Done():
     return ctx.Err()
    case <-time.After(time.Duration(retryCount) * time.Second):
     retryCount++
     continue
 }
}
//错误后直接 panic
if err := getUser(); err != nil {
    panic("something wrong") 
}

项目中使用 Error 的方案

方案一 :定义 ErrorMessage 包

项目中可以定义一个含有所有错误信息的包,然后在使用时直接引用。

package errorMsg
import "errors"
var UndefinedError = errors.New("未定义")
var InvalidateParamsError = errors.New("不合法参数")
// main 函数中使用
func getUser() error {
    return errorMsg.UndefinedError
}

方案二:自定义 error 类型

type ParamNotFound NotFound
type ValueNotFound NotFound
type NotFound struct {
	msg string
}
func (n *NotFound) Error() string {
	return n.msg
}
// 通过使用 switch 处理不同类型的错误,
func handleErrors(err error) {
  switch v := err.(type) {
  case ParamNotFound:
    fmt.Printf("ParamNotFound: %v\n", v)
  case ValueNotFound:
    fmt.Printf("ValueNotFound: %v\n", v)
  default:
    fmt.Printf("Other error: %v\n", v)
  }
}

最后

有效的错误处理对于构建可靠的服务至关重要,个人觉得最佳的实践是团队内部的统一规范。没有最佳实践和方案,只有最适合的你的场景的实践。

以上就是Golang实践之Error创建和处理详解的详细内容,更多关于Golang Error创建和处理的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文