想象一个大型项目,它处理由其自己的 goroutine 处理的大量并发请求。碰巧代码中存在错误,其中一个请求将因零引用而导致恐慌。
在 Java、C# 和许多其他语言中,这最终会导致异常,该异常会停止请求,而不会对其他正常请求造成任何损害。在 Go 中,这会使整个程序崩溃。
AFAIK,我必须recover()为每一个新的 goroutine 创建。这是防止整个程序崩溃的唯一方法吗?
更新:recover()为每个 gorouting 创建添加调用似乎没问题。第三方库呢?如果第三方在没有recover()安全网的情况下创建 goroutine,似乎没有什么可做的。
如果您选择推迟恢复所有内容,我建议您花一些时间来确保收集到清晰的错误消息以及足够的信息以便立即采取行动。
将恐慌消息写入 stderr/stdout 并不是很好,因为很难找到问题所在。根据我的经验,最好的方法是投入一些时间让你的 Go 程序以合理的方式处理错误。errors.Wrap例如,来自“ github.com/pkg/errors ”允许您包装所有错误并获取堆栈跟踪。
恢复恐慌往往是一种必要的罪恶。就像你说的,仅仅因为一个请求引起恐慌而使整个程序崩溃是不理想的。在大多数情况下,恢复恐慌不会适得其反,但程序可能最终处于未定义的不可恢复状态,只有手动重新启动才能修复。话虽这么说,在这种情况下我的建议是确保你的 Go 程序公开一种创建核心转储的方法。
以下是当 SIGQUIT 发送到 Go 程序时如何将核心转储写入 stderr(例如kill pid -QUIT)
go func() {
// Based on answers to this stackoverflow question:
// /sf/ask/1336586961/
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGQUIT)
for {
<-sigs
fmt.Fprintln(os.Stderr, "=== received SIGQUIT ===")
fmt.Fprintln(os.Stderr, "*** goroutine dump...")
var buf []byte
var bufsize int
var stacklen int
// Create a stack buffer of 1MB and grow it to at most 100MB if
// necessary
for bufsize = 1e6; bufsize < 100e6; bufsize *= 2 {
buf = make([]byte, bufsize)
stacklen = runtime.Stack(buf, true)
if stacklen < bufsize {
break
}
}
fmt.Fprintln(os.Stderr, string(buf[:stacklen]))
fmt.Fprintln(os.Stderr, "*** end of dump")
}
}()
Run Code Online (Sandbox Code Playgroud)