如何防止Go程序在意外恐慌后崩溃?

时间:2018-05-18 20:35:25

标签: go

想想一个大型项目,它处理由自己的goroutine处理的大量并发请求。碰巧代码中存在一个错误,其中一个请求会因为nil引用而引起恐慌。

在Java,C#和许多其他语言中,这最终会导致异常停止请求,而不会对其他健康请求造成任何伤害。在go中,这会使整个程序崩溃。

AFAIK,我必须为每一个新的例程创建recover()。这是阻止整个程序崩溃的唯一方法吗?

更新:为每个gorouting创建添加recover()调用似乎没问题。第三方图书馆怎么样?如果第三方创建没有recover()安全网的goroutines,似乎没有什么可做的。

2 个答案:

答案 0 :(得分:1)

如果你去推迟恢复,我建议花一些时间来确保收集到明确的错误信息,并提供足够的信息以便迅速采取行动。

将恐慌消息写入stderr / stdout并不是很好,因为很难找到问题所在。根据我的经验,最好的方法是花一点时间让Go程序以合理的方式处理错误。来自" github.com/pkg/errors" errors.Wrap例如,允许您包装所有错误并获得堆栈跟踪。

恢复恐慌通常是必要的恶魔。就像你说的那样,仅仅因为一个请求引起了恐慌而导致整个程序崩溃并不理想。在大多数情况下,恢复恐慌不会反击,但程序可能最终处于未定义的不可恢复状态,只有手动重启才能修复。话虽这么说,我在这种情况下的建议是确保你的Go程序暴露出一种创建核心转储的方法。

以下是当SIGQUIT发送到Go程序时如何将核心转储编写到stderr(例如kill pid -QUIT

go func() {
    // Based on answers to this stackoverflow question:
    // https://stackoverflow.com/questions/19094099/how-to-dump-goroutine-stacktraces
    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")
    }
}()

答案 1 :(得分:0)

没有恢复功能你无法处理恐慌,一个好的做法是使用类似功能的中间件来保护你的安全功能,结帐这个片段

{{3}}