Go 语言中的错误处理
通过模块化组件的方式,学习 Go 语言的微服务
Go 语言的错误处理
Go 语言中的 error 定义如下
1 |
|
在 Go 1.13 版本之前,我们通常使用的是 errors.New()
来进行返回一个 error 对象指针,相应的源码如下所示:
1 |
|
关于 Go 中的指针与 C 中指针大致相同,都是指向某块内存地址值,可以通过 * 解引用的方式进行使用。至于此处为什么错误类型需要返回一个指针变量,如下解释:
主要是防止因为相同的字符串进行匹配,导致错误类型区别不准而导致的问题
可能第一次听会难以理解,我们通过代码来解释,来看以下的代码:
1 |
|
以上代码输出的是 Named Type Error
,因为 var ErrorStructType = errors.New("EOF")
与 if ErrorStructType == errors.New("EOF")
虽然字符串的值相同,但是因为返回的是一个指针地址,地址不同的指针进行比较,当然也不同。所以这就是为什么要返回指针的原因。
其中 panic
与 error
的区别,主要是 error
类型定义仅仅只作为值类型进行处理,其抛出错误,证明业务进行到此处,有错误,但是不影响全局,程序员在后续能进行处理。
而 panic
一般表示不可恢复类的错误,比如栈溢出、索引越界等程序员在后续无法进行处理的问题,必须修改代码才能解决问题。
错误类型
一般错误类型有以下几种:
1、Sentinel Error
预定义的错误,如 io.EOF
、syscall.ENOENT
。使用预定义的错误相当的不方便,所以尽可能避免使用预定义的错误,如果实在要使用,需要联系资深的程序员,进行 code review
。
2、Error types
使用 error interface
接口的自定义类型,定义后可以包装底层的错误,提供更多的上下文与堆栈的信息,方便程序员进行调试、定位问题。但是使用这种 interface 接口进行实现的 API 需要将 error 变成 public 字段,会导致和调用者产生强耦合,从而导致 API 变得脆弱。
所以还是得尽量避免使用 error types
3、Opaque errors
最为灵活的错误处理,主要是只返回错误,但是错误内部不透明,一般建议 API 的设计,对外只暴露一个 error 类型的返回值即可!
go 1.13 版本前的错误处理
一般的错误处理模式基本上是 if err != nil {}
这种常见的模式。
所以此处博主只写,博主建议的事情:
1、无第三方库协作的时候,简单使用 errors.New()
或者 errors.Errorf()
两个函数进行方法的处理。
2、如果需要配合 github 开源第三方包以及公司中台组件内部的包,使用 errors.Wrap()
与 errors.Wrapf()
保存堆栈信息,最终集中化输出所有日志,而不是遇到错误就打印日志。
go 1.13 版本后的错误处理
go 在 1.13 版本给 errors
包新增了两个函数方法:
errors.Is() 与 errors.As()
并在 fmt
包中也新增了 fmt.Errorf()
函数方法。
用于向错误中添加错误字段,占位符为 %w
,用于支持以上两个 errors
方法。具体使用案例如下
1 |
|
go 日志错误处理注意事项
1、关于 log.Fatal()
使用只推荐在 go 项目 init 函数中使用,或者main 函数内初始化配置失败时候使用!因为 log.Fatal()
函数会调用 os.Exit(1)
,此种情况下会导致程序直接退出,defer 函数都不会执行,不利于程序排查问题!
uber_go_guide 错误处理
1、若调用者需要自己处理其中抛出的错误,采用 error.As() 或 error.Is() 函数合适。
2、错误如果不需要匹配,则如果是静态字符串,则使用 errors.New 进行抛出即可,若是动态字符串信息,则使用 fmt.Errorf 或者自定义的错误。
错误匹配? | 错误消息 | 指导 |
---|---|---|
No | static | errors.New |
No | dynamic | fmt.Errorf |
Yes | static | top-level var with errors.New |
Yes | dynamic | custom error type |