Exception 如何从外部包中的异步死机中恢复

Exception 如何从外部包中的异步死机中恢复,exception,go,exception-handling,panic,Exception,Go,Exception Handling,Panic,我正在学习围棋,并试图了解如何正确处理来自外部软件包的恐慌 下面是一个可运行的示例,假设一个包定义了doFoo方法。(为了示例,它位于同一个包中) 调用doFoo方法将使服务器崩溃,我认为这是正确的行为,因为应用程序现在处于破坏状态。最好是崩溃,并通过一些负载平衡器将后续请求转发到不同的进程。 但是,我的api服务器可能仍然在为其他客户机服务,它可能在维护WebSocket,我可能还想在这里返回500个错误 来自nodejs,我习惯了uncaughtException的概念,用于处理未捕获的同步

我正在学习围棋,并试图了解如何正确处理来自外部软件包的恐慌

下面是一个可运行的示例,假设一个包定义了
doFoo
方法。(为了示例,它位于同一个包中)

调用doFoo方法将使服务器崩溃,我认为这是正确的行为,因为应用程序现在处于破坏状态。最好是崩溃,并通过一些负载平衡器将后续请求转发到不同的进程。 但是,我的api服务器可能仍然在为其他客户机服务,它可能在维护WebSocket,我可能还想在这里返回500个错误

来自nodejs,我习惯了
uncaughtException
的概念,用于处理未捕获的同步异常和
unhandledrefraction
处理未捕获的异步异常。这两种进程构造使开发人员可以选择立即使程序崩溃(如果有意义的话),或者记录错误,返回正确的http代码,然后在需要时优雅地关闭

在我的在线研究中,我发现很多资源都在说,恐慌不像例外,它们是不寻常的,你不需要担心它们。但在编写代码时,似乎很容易引起恐慌。这完全取决于开发人员,以确保他的库不会恐慌,这里100%涉及人为因素

这让我想知道,我是否需要审核我将要使用的每个包的整个代码库,包括所有的包依赖项?仅仅因为我没有办法防止某个外部软件包中丢失的恢复,它会破坏我的整个服务器并破坏我的用户体验

或者,当库代码中发生异步死机时,是否存在一些我不知道的策略可以正常地失败

我注意到从1.8开始就有正常的关机,但我不能使用它,因为我的程序已经崩溃了。

有一个gorilla恢复处理程序,但同样,这只能防止同步恐慌。

更新:

我知道恐慌并非例外。重申这并不能回答问题,恐慌和例外并不是这个问题的核心。这个问题是关于理解该语言可以提供什么工具来强制执行边界,而不需要让开发人员阅读整个包树中的每一行。如果在语言中不可能,那么说明这是一个有效的答案。我只是不知道是不是。恐慌也不例外。不要把他们当作例外,你会没事的

第一件事:包API永远不应该恐慌,它们应该总是返回错误,除非在某些非常罕见的情况下,然后必须清楚地记录它们恐慌的时间和原因(
regexp.MustCompile
是可能恐慌的一个很好的例子)。如果任何程序包遇到错误(并且没有很好的理由这样做)而惊慌失措,那么它就是坏的,不要使用它

如果您进行边界检查,请确保不要访问零指针,等等,您不必担心恐慌

至于在goroutine中恢复恐慌,除非goroutine有自己的恢复处理程序,否则你不能

如果goroutine来自第三方库,请不要使用该库!如果他们不够松懈,没有检查边缘情况和/或懒惰到只在出现错误时惊慌失措,那么您为什么要使用他们的代码?谁知道它还有什么地雷


如果goroutine是您自己的代码,请尝试消除可能引起恐慌的事情,然后添加一个恢复处理程序,以便在需要时捕获您无法阻止的事情。

您是说,是的,您必须审核每个包,包括每个子包?这种语言似乎无法保证任何一个包都不会惊慌失措。如果您使用的是可能写得不好的粗略包,那么是的。无论它们是用什么语言编写的,您都希望对它们进行审核。在实际使用中,如果您使用的是质量良好的流行软件包,我想您会发现问题远没有您想象的那么严重。一个典型的项目有10-20多个依赖项,每个项目可能还有5-10多个依赖项。我们说的是十万行的开源代码。现在我有一位客户问我是否可以保证服务器不会随机崩溃并切断所有用户,我可以用nodejs说是的。当我的项目使用Go时,我应该回答什么?这就是这个问题的意义所在,任何回避的答案都会让人分心。对于中等规模的服务器端项目,它本身是否可靠?是时候写一个监视器了?(监视器是一种应用程序,它可以监视你的应用程序,如果应用程序失败,它会自动重新启动。)不要试图成为一个聪明的傻瓜,但你不能保证一个应用程序永远不会崩溃,这在现实世界中是100%不可能的,不管是哪种语言。我不希望受到任何可能的程序崩溃的保护。我只是想保护自己不受人为错误的伤害。nodejs为同步和异步代码路径提供了一种实现这一点的方法。Go确实为同步代码路径提供了一种实现这一点的方法,但对异步代码路径却没有。这似乎是丢失的一块,我正在试图理解为什么。简言之,你没有。意外的恐慌会使应用程序崩溃,而预期的过程是重新启动它。
package main

import (
    "log"
    "net/http"

    "sync"
    "time"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
)
// Method from External package
func doFoo() {
    var wg sync.WaitGroup
    wg.Add(1)
    // Do some cool async stuff
    go func() {
        time.Sleep(500)
        wg.Done()
        panic("Oops !")
    }()
}

func router() *mux.Router {
    var router = mux.NewRouter().StrictSlash(true)
    router.HandleFunc("/doFoo", index).Methods("GET")
    return router
}

func main() {
    log.Fatal(http.ListenAndServe(":8080", handlers.RecoveryHandler()(router())))
}

func index(w http.ResponseWriter, r *http.Request) {
    defer func() {
        recover()
        w.WriteHeader(http.StatusInternalServerError)
    }()
    doFoo()
    w.WriteHeader(http.StatusOK)
}