golang异常处理详解

Compound新激励政策生效后,Dai取代BAT成为平台上最热门借贷资产

比特币日报讯,据CoinDesk消息,随着Compound新的激励政策生效,目前收益最高的流动性挖矿资产为Dai,矿工会从Compound中借出Dai然后重新存入,通过进行多次重复操作,可获得高达40%的收益。Compound上,Dai目前已成为最为热门的借贷…

Python实战社群

Java实战社群

长按识别下方二维码,按需求添加

golang异常处理详解

扫码关注添加客服

进Python社群▲

golang异常处理详解

扫码关注添加客服

进Java社群

作者丨加班的小熊 
来源丨编程三分钟(coding3min

今天想和大家聊聊 golang 的异常处理

异常处理思想

go 语言里是没有 try catch 的概念的,因为 try catch 会消耗更多资源,而且不管从 try 里面哪个地方跳出来,都是对代码正常结构的一种破坏。
所以 go 语言的设计思想中主张
  • 如果一个函数可能出现异常,那么应该把异常作为返回值,没有异常就返回 nil
  • 每次调用可能出现异常的函数时,都应该主动进行检查,并做出反应,这种 if 语句术语叫卫述语句
所以异常应该总是掌握在我们的手上,保证每次操作产生的影响达到最小,保证程序即使部分地方出现问题,也不会影响整个程序的运行,及时的处理异常,这样就可以减轻上层处理异常的压力。
同时也不要让未知的异常使你的程序崩溃。

异常的形式

我们应该让异常以这样的形式出现
func Demo() (int, error)
我们应该让异常以这样的形式处理(卫述语句)
_,err := errorDemo()
 if err!=nil{
  fmt.Println(err)
  return
 }

自定义异常

比如程序有一个功能为除法的函数,除数不能为 0 ,否则程序为出现异常,我们就要提前判断除数,如果为 0 返回一个异常。那他应该这么写。
func divisionInt(a, b int) (int, error) {
 if b == 0 {
  return -1, errors.New("除数不能为0")
 }

 return a / b, nil
}
这个函数应该被这么调用
a, b := 40
res, err := divisionInt(a, b)
if err != nil {
 fmt.Println(err.Error())
 return
}
fmt.Println(a, "除以", b, "的结果是 ", res)
可以注意到上面的两个知识点
  • 创建一个异常 errors.New("字符串")
  • 打印异常信息 err.Error()
只要记得这些,你就掌握了自定义异常的基本方法。
但是 errors.New("字符串") 的形式我不建议使用,因为他不支持字符串格式化功能,所以我一般使用 fmt.Errorf 来做这样的事情。
err = fmt.Errorf("产生了一个 %v 异常""喝太多")

详细的异常信息

上面的异常信息只是简单的返回了一个字符串而已,想在报错的时候保留现场,得到更多的异常内容怎么办呢?这就要看看 errors 的内部实现了。其实相当简单。
errors 实现了一个叫 error 的接口,这个接口里就一个 Error 方法且返回一个 string ,如下
type error interface {
 Error() string
}
只要结构体实现了这个方法就行,源码的实现方式如下
type errorString struct {
 s string
}

func (e *errorString) Error() string {
 return e.s
}

// 多一个函数当作构造函数
func New(text string) error {
 return &errorString{text}
}
所以我们只要扩充下自定义 error 的结构体字段就行了。
这个自定义异常可以在报错的时候存储一些信息,供外部程序使用
type FileError struct {
 Op   string
 Name string
 Path string
}
// 初始化函数
func NewFileError(op string, name string, path string) *FileError {
 return &FileError{Op: op, Name: name, Path: path}
}
// 实现接口
func (f *FileError) Error() string {
 return fmt.Sprintf("路径为 %v 的文件 %v,在 %v 操作时出错", f.Path, f.Name, f.Op)
}
调用
f := NewFileError("读""README.md""/home/how_to_code/README.md")
fmt.Println(f.Error())
输出
路径为 /home/how_to_code/README.md 的文件 README.md,在 读 操作时出错

defer

上面说的内容很简单,在工作里也是最常用的,下面说一些拓展知识。
Go 中有一种延迟调用语句叫 defer 语句,它在函数返回时才会被调用,如果有多个 defer 语句那么它会被逆序执行。
比如下面的例子是在一个函数内的三条语句,他是这么怎么执行的呢?
defer fmt.Println("see you next time!")
defer fmt.Println("close all connect")
fmt.Println("hei boy")
输出如下, 可以看到两个 defer 在程序的最后才执行,而且是逆序。
hei boy
close all connect
see you next time!
这一节叫异常处理详解,终归是围绕异常处理来讲述知识点, defer 延迟调用语句的用处是在程序执行结束,甚至是崩溃后,仍然会被调用的语句,通常会用来执行一些告别操作,比如关闭连接,释放资源(类似于 c++ 中的析构函数)等操作。
涉及到 defer 的操作
  • 并发时释放共享资源锁
  • 延迟释放文件句柄
  • 延迟关闭 tcp 连接
  • 延迟关闭数据库连接
这些操作也是非常容易被人忘记的操作,为了保证不会忘记,建议在函数的一开始就放置 defer 语句。

panic

刚刚有说到 defer 是崩溃后,仍然会被调用的语句,那程序在什么情况下会崩溃呢?
Go 的类型系统会在编译时捕获很多异常,但有些异常只能在运行时检查,如数组访问越界、空指针引用等。这些运行时异常会引起 painc 异常(程序直接崩溃退出)。然后在退出的时候调用当前 goroutinedefer 延迟调用语句。
有时候在程序运行缺乏必要的资源的时候应该手动触发宕机(比如配置文件解析出错、依赖某种独有库但该操作系统没有的时候)
defer fmt.Println("关闭文件句柄")

panic("人工创建的运行时异常")
报错如下
golang异常处理详解

报错示例

panic recover

出现 panic 以后程序会终止运行,所以我们应该在测试阶段发现这些问题,然后进行规避,但是如果在程序中产生不可预料的异常(比如在线的web或者rpc服务一般框架层),即使出现问题(一般是遇到不可预料的异常数据)也不应该直接崩溃,应该打印异常日志,关闭资源,跳过异常数据部分,然后继续运行下去,不然线上容易出现大面积血崩。
然后再借助运维监控系统对日志的监控,发送告警给运维、开发人员,进行紧急修复。
语法如下:
func divisionIntRecover(a, b int) (ret int) {
 defer func() {
  if err := recover(); err != nil {
   // 打印异常,关闭资源,退出此函数
   fmt.Println(err)
   ret = -1
  }
 }()

 return a / b
}
调用
var res int
 datas := []struct {
  a int
  b int
 }{
  {20},
  {22},
 }

 for _, v := range datas {
  if res = divisionIntRecover(v.a, v.b); res == -1 {
   continue
  }
  fmt.Println(v.a, "/", v.b, "计算结果为:", res)
 }
输出结果
runtime error: integer divide by zero
2 / 2 计算结果为:1
  • 调用 panic 后,当前函数从调用点直接退出
  • recover 函数只有在 defer 代码块中才会有效果
  • recover 可以放在最外层函数,做统一异常处理。
这就是 go 异常处理,我所能想到和找到的全部内容了,希望你在工作中用的更顺手。
小熊虽然工作忙,文章没办法发的那么频繁,但是我有时间就写一点,反复校对,代码也反复测试最后放 github 上,这样文章的内容会更完整、更有逻辑、更少异常、对读者对自己都更负责。如果你发现了文章中出现问题,欢迎在评论区和我讨论,非常感谢!
golang异常处理详解
程序员专栏
 扫码关注填加客服 
长按识别下方二维码进群
golang异常处理详解

golang异常处理详解

近期精彩内容推荐:  

golang异常处理详解 程序员背着电脑送外卖,送单途中帮人修复bug

golang异常处理详解 一个员工的离职成本,很恐怖!

golang异常处理详解 这款网络排查工具,堪称神器!

golang异常处理详解 原来可视化还能这么美...


golang异常处理详解


golang异常处理详解

在看点这里golang异常处理详解好文分享给更多人↓↓

在比特币日报读懂区块链和数字货币,加入Telegram获得第一手区块链、加密货币新闻报道。

瑞士银行InCore Bank AG为Kraken用户开通欧元渠道

比特币日报讯,据CoinDesk消息,瑞士银行InCore Bank AG为Kraken开通欧元渠道,开通完成后Kraken用户可以使用欧元购买数字货币。据悉,就管理资产规模而言该银行在瑞士排第209名。在比特币日报读懂区块链和数字货币,加入Telegram获…

Click to rate this post!
[Total: 0 Average: 0]

人已赞赏
Go语言技术开发

为什么大厂都在用 GO 语言?读透 GO 语言的切片

2020-7-7 18:44:24

Go语言技术开发

基于DDD的golang实现

2020-7-7 18:47:34

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索