Golang笔记--panic异常捕获

golang没有像其他语言那样提供try{}catch(){}方式去处理异常,但是可以通过panic和recover捕获处理异常。

在程序运行中,无论是显式或者隐式触发了panic,它会立即停止当前函数的执行,并沿着函数调用栈向上传播,直到被捕获或达到顶层的main函数,导致程序终止。

如下面的案例,第一个testFunc调用会执行,中间调用了panicFunc,触发了panic,下面的testFunc就不会被执行。

func testFunc() {
	fmt.Println("this is test func")
}
func panicFunc() {
	panic("this is panic")
}

func main() {
	testFunc()
	panicFunc()
	testFunc()
}

这时,如下面案例,我们需要对testFunc进行recover捕获处理,再次编译运行,将不影响最后一个testFunc的执行。

func testFunc() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()
	panic("this is panic")
}

但是有时候需要给客户端返回这个异常,这种属于主动去调用panic,但是有时候系统触发的异常,想捕获返回给客户端怎么处理呢,比如两个数相除,除数不能是0,我们可以定义一个error的返回类型。判断err是否空,返回给客户端。

func testFn(n, m int) (cal int, err error) {
	defer func() {
		r := recover()
		if r != nil {
			//TODO可以执行一些监控接口,通知异常
			err = fmt.Errorf("Panic error: %v", r)
		}
	}()
	cal = n / m
	return
}
func main() {
	cal, err := testFn(5, 0)
	fmt.Println(err)
	fmt.Println(cal)
}
//输出
Panic error: runtime error: integer divide by zero
0

对于一些非系统的异常,可以通过自定义的错误信息抛出panic返回。例如做一些订单的校验业务

func checkName(name string) (err error) {
	if name == "iamzcr" {
		return nil
	} else {
		return errors.New("name error")
	}
}
func checkOrder(orderNum string) (err error) {
	if orderNum == "123456" {
		return nil
	} else {
		return errors.New("order num error")
	}
}
func check(orderData map[string]string) (err error) {
	defer func() {
		r := recover()
		if r != nil {
			//TODO可以执行一些监控接口,通知异常
			err = fmt.Errorf("Panic error: %v", r)
		}
	}()
	err = checkName(orderData["name"])
	if err != nil {
		panic(err)
	}
	err = checkOrder(orderData["order_num"])
	if err != nil {
		panic(err)
	}
	return
}

func main() {
	var orderData = make(map[string]string)
	orderData["name"] = "iamzc1r"
	orderData["order_num"] = "123456"
	err := check(orderData)
	if err == nil {
		fmt.Println("order check success")
	} else {
		fmt.Println("order check fail msg:", err)
	}
}

如果在go协程里面,如果不处理可能潜在的panic,不仅是一个协程异常,其他主进程和其他协程也会退出,下面的程序,开辟了5个协程,处理的业务是两个数相除,如果在函数里面testGoFn不处理系统抛出的异常,会导致整个程序奔溃。

func testGoFn(n, m int) {
	defer func() {
		r := recover()
		if r != nil {
			//TODO可以执行一些监控接口,通知异常
			fmt.Printf("Panic error: %v\n", r)
		}
	}()
	fmt.Printf("cal: %v\n", n/m)
}

func main() {
	fmt.Println("main start")

	for i := 0; i < 5; i++ {
		n := rand.Intn(3) // 生成0到2之间的随机整数
		m := rand.Intn(3) // 生成0到2之间的随机整数
		go testGoFn(n, m)
	}
	time.Sleep(2 * time.Second)
	fmt.Println("main end")
}

小结

当程序发生panic时,它会立即停止当前函数的执行,并沿着函数调用栈向上传播,直到被捕获或达到顶层的main函数,导致程序终止。

如果在defer语句中调用了recover,并且在同一协程中发生了panic,那么recover将返回panic的值,程序将继续执行后续的语句,而不是终止程序。


上一篇:记录golang常用库-json处理

下一篇:linux环境PostgreSQL安装

关注公众号

发表评论