golang错误处理与恢复

Go语言没有异常机制,而是使用error类型进行错误处理。自定义错误可以提供更清晰的错误信息,而最佳实践是尽早失败并使用自定义类型。panic用于不可恢复的错误,会在执行defer后退出。recover在defer中使用可以捕获并恢复panic,但过度使用可能导致服务稳定性问题。

go的错误机制

  • golang没有异常机制
  • error类型实现了异常接口
type error interface {
  Error() string
}
  • 可以通过errors.New快速创建错误实例
errors.New("n must be ddxfsefsdf")

废话不多说, 来了解下go兼并python的优雅

import (
	"errors"
	"testing"
)

func GetFebnacci(n int) ([]int, error) {
	if n < 2 || n >= 100 {
		return nil, errors.New("n should be in [2, 100]")
	}

	fiblist := []int{1, 1}

	for i := 2; i < n; i++ {
		fiblist = append(fiblist, fiblist[i-2]+fiblist[i-1])
	}
	return fiblist, nil
}

func TestGetFebnacci(t *testing.T) {
	//t.Log(GetFebnacci(10))
	if v,err:=GetFebnacci(100);err!=nil{
		t.Error(err)
	}else {
		t.Log(v)
	}
}


最后输出:

=== RUN   TestGetFebnacci
    error_test.go:24: n should be in [2, 100]
--- FAIL: TestGetFebnacci (0.00s)

FAIL

注意下写法

自定义错误

我们为了开发时候能更清楚是哪里报错了,且封装一些信息出去,我们更倾向于自定义一些错误类型进行输出error msg
不多说: 上代码

var LessThanTwoError = errors.New("n should not less than 2")
var LargeThanHundredError = errors.New("n should not large than 100")

那么错误判断可以修改:

func GetFebnacci(n int) ([]int, error) {
	if n < 2 {
		return nil, LessThanTwoError
	}
	if n >= 100 {
		return nil, LargeThanHundredError
	}

	fiblist := []int{1, 1}

	for i := 2; i < n; i++ {
		fiblist = append(fiblist, fiblist[i-2]+fiblist[i-1])
	}
	return fiblist, nil
}
  • go倾向于快速报错,将容易出错的放在最前面
    测试:
func TestGetFebnacci(t *testing.T) {
	//t.Log(GetFebnacci(10))
	if v, err := GetFebnacci(100); err != nil {
		if err == LessThanTwoError{
			fmt.Println("it is less")
		}
		t.Error(err)
	} else {
		t.Log(v)
	}
}

最佳实践

其实错误处理的最佳实践就是上面的,倾向于用自定义类型去处理,以便于判断错误类型

  • 及早失败,避免嵌套也是很重要的
    那正确的写法应该是: 如果有错误,我们dosth return,最后,没有错误,我们才执行正确的做法

panic和recover

panic

  • panic:用于不可恢复的错误
  • panic:退出前会执行defer指定的内容

说到退出,我们想到了啥?
os.Exit()
老手懂的都懂,

  • os比较牛逼,退出前是不会执行defer的内容的
  • os.Exit退出时候,不会输出当前调用栈信息

看代码:

func TestPanicVsExit(t *testing.T) {
	fmt.Println("start")
	os.Exit(-1)
}

这段代码输出:

=== RUN   TestPanicVsExit
start


Process finished with the exit code 1

panic接收一个空接口,通常我们会传递一个错误进去:

func TestPanicVsExit(t *testing.T) {
	fmt.Println("start")
	panic(errors.New("something wrong"))
}

输出:

=== RUN   TestPanicVsExit
start
--- FAIL: TestPanicVsExit (0.00s)
panic: something wrong [recovered]
	panic: something wrong
// 省略 go routine 的错误栈!!
  • 你运行下就知道,panic是会输出调用栈的信息的
  • os.Exit不会,啥都没

加上defer函数

func TestPanicVsExit(t *testing.T) {
	defer func() {
		fmt.Println("Finally!")
	}()
	fmt.Println("start")
	panic(errors.New("something wrong"))
}

看下输出:

=== RUN   TestPanicVsExit
start
Finally!
--- FAIL: TestPanicVsExit (0.00s)
panic: something wrong [recovered]
	panic: something wrong

goroutine 6 [running]:
// 省略调用栈的信息
  • 看到了吧~ defer函数是会在退出前执行的

recover
还记得我们写的try...catch...,我们不希望程序中断,写个最基类的错误进去捕捉~: 这样其实是不好的
那么go里面也可以的:
defer函数不是会在panic前延迟执行吗? 那我们可以在defer里进行recover,处理下错误

defer func(){
  if err:=recover();err!=nil{
    // 恢复错误
  }
}()

上代码demo吧:

func TestPanicVsExit(t *testing.T) {

	defer func() {
		if err:=recover(); err != nil{
			fmt.Println("recover from:", err)
		}
	}()

	fmt.Println("start")
	//os.Exit(-1)
	panic(errors.New("something wrong"))
}

输出是什么呢?

=== RUN   TestPanicVsExit
start
recover from: something wrong
--- PASS: TestPanicVsExit (0.00s)
PASS

Process finished with the exit code 0

没有再报错了:pass 了
那我们可以说: panic被recover了(恐慌痛苦被恢复了? 怪怪的)

但是:方才不是说catch是不好?会出现什么问题?

  • 容易导致僵尸服务进程,导致heal check失效
  • let it crash ,让它报错,往往是我们恢复不确定性错误的最好方法
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值