go panic and recover

go panic and recover

foreversmart write on 2018-09-20
因为最近发现 GO协程下的任务会 panic 导致程序 crash 所以对 go 函数进行了封装
type HandleFunc func() func Go(handleFunc HandleFunc) { go func() { defer func() { if r != nil { // 原生logger var logger *log.Logger logger = log.New(os.Stderr, "\n\n\x1b[31m", log.LstdFlags) // qvm logger msg := fmt.Sprintf("[Recovery] panic recovered:\n%s\n%s%s", r, stack, reset) logger.Println(msg) } }() handleFunc() }() }
Go
有了我们自定义的 GO 函数以后就对线上的代码进行修改
for _, b:= persons{ go func(a *Person) { fmt.Println(a) }(b) }
Go
修改为
for _, b:= persons{ Go(func(){ func(a *Person) { fmt.Println(a) }(b)) }
Go
但是发现 线上出现了诡异的问题,线上一直打印的是最后一个用户 仔细一看原来是GO lang 闭包 保留的是 b 的引用,所以协程 func()里面的 Person 对象都是数组最后一个的引用
第二个关于闭包问题:
func CatchRecover(r interface{}) { if r != nil { // 原生logger var logger *log.Logger logger = log.New(os.Stderr, "\n\n\x1b[31m", log.LstdFlags) // qvm logger fields := make(map[string]interface{}) fields["CLASS"] = enums.PANICLoggerClass.String() qvmLogger := utils.NewEmptyLoggerWithFields(fields).AddDefaultHook() stack := qvm_stack.Stack(4) msg := fmt.Sprintf("[QVM Recovery] panic recovered:\n%s\n%s%s", r, stack, reset) logger.Println(msg) qvmLogger.Error(msg) } }
Go
我们对于 panic 写了一个统一的处理 recover 结果的封装函数,用于打印出 panic 时程序的调用栈方便 debug 对于这个 CatchRecover 我们的用法有下面两种: 1:
defer func() { CatchRecover(recover()) }()
Go
2:
defer CatchRecover(recover())
Go
结果发现第一种方式能正常的 recover panic, 第二种程序还是会 panic 阅读 recover 官方说明:
上面提到 recover 如果不在defer function 里面就不会 阻止程序 panic 所以针对上段代码解读: 第一种方式是一个函数闭包的方式将执行体压入 defer 栈中 第二种方式是将函数 CatchRecover 压入 defer 栈中,这个过程中函数的参数 recover 会在这个时候执行,而不是在 defer function 中执行的,所以这个时候的 recover 不会阻止 panic 并且也 recover 不到任何值

「真诚赞赏,手留余香」

Foreversmart

真诚赞赏,手留余香

使用微信扫描二维码完成支付