这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记
错误和异常是两个不同的概念,非常容易混淆。在本次的课程内容及极简版抖音开发和Debug的过程中,我更加深刻地体会到了两者的区别,在这里系统记录一下。
错误和异常的区别
错误是业务过程的一部分,而异常不是。
错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在意料之中;而异常指的是不应该出现问题的地方出现了问题,比如引用了空指针,这种情况在意料之外。
Golang中引入error接口类型作为错误处理的标准模式,如果函数要返回错误,则返回值类型列表中肯定包含error。error处理过程类似于C语言中的错误码,可逐层返回,直到被处理。
Golang中引入两个内置函数panic和recover来触发和终止异常处理流程,同时引入关键字defer来延迟执行defer后面的函数。 一直等到包含defer语句的函数执行完毕时,延迟函数(defer后的函数)才会被执行,而不管包含defer语句的函数是通过return的正常结束,还是由于panic导致的异常结束。你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反。
当程序运行时,如果遇到引用空指针、下标越界或显式调用panic函数等情况,则先触发panic函数的执行,然后调用延迟函数。调用者继续传递panic,因此该过程一直在调用栈中重复发生:函数停止执行,调用延迟执行函数等。如果一路在延迟函数中没有recover函数的调用,则会到达该携程的起点,该携程结束,然后终止其他所有携程,包括主携程(类似于C语言中的主线程,该携程ID为1)。 错误和异常从Golang机制上讲,就是error和panic的区别。
错误和异常的转化
Golang错误和异常是可以互相转换的:
- 错误转异常,比如逻辑上尝试请求某个URL,最多尝试三次,尝试三次的过程中请求失败是错误,尝试完第三次还不成功的话,失败就被提升为异常了。
- 异常转错误,比如panic触发的异常被recover恢复后,将返回值中error类型的变量进行赋值,以便上层函数继续走错误处理流程。
错误的正确处理方式
- 失败原因只有一个时,不用error,如下代码。
func (self *AgentContext) CheckHostType(host_type string) error {
switch host_type {
case "virtual_machine":
return nil
case "bare_metal":
return nil
}
return errors.New("CheckHostType ERROR:" + host_type)
}
// 可以看出该函数失败的原因只有一个,所以返回值的类型应该为bool,而不是error,重构一下代码
func (self *AgentContext) IsValidHostType(hostType string) bool {
return hostType == "virtual_machine" || hostType == "bare_metal"
}
- 没有失败时,不使用error
- error应放在返回值类型列表的最后
- 错误值和错误信息统一定义,而不是跟着感觉走,如下:
var (
Success = NewErrNo(SuccessCode, "Success")
ServiceErr = NewErrNo(ServiceErrCode, "Service is unable to start successfully")
ParamErr = NewErrNo(ParamErrCode, "Wrong Parameter has been given")
LoginErr = NewErrNo(LoginErrCode, "Wrong username or password")
UserNotExistErr = NewErrNo(UserNotExistErrCode, "User does not exists")
UserAlreadyExistErr = NewErrNo(UserAlreadyExistErrCode, "User already exists")
)
const (
SuccessCode = 0
ServiceErrCode = 10001
ParamErrCode = 10002
LoginErrCode = 10003
UserNotExistErrCode = 10004
UserAlreadyExistErrCode = 10005
)
- 错误逐层传递时,层层都加日志
- 当发生错误时,不忽略有用的返回值
对待异常该怎么做
- 在开发阶段尽量让他挂掉。只有挂才会第一时间知道错误。在早期开发以及任何发布阶段之前,最简单的同时也可能是最好的方法是调用panic函数来中断程序的执行以强制发生错误,使得该错误不会被忽略,因而能够被尽快修复。
- 在部署发布之后,应该尽量避免挂掉的情况,免得无法提供服务或者影响其他服务。