如何从外部软件包中的异步恐慌中恢复

问题描述:

我在学习Go并试图理解如何正确处理来自外部软件包的恐慌。如何从外部软件包中的异步恐慌中恢复

这是一个可运行的示例,比如一个包定义了doFoo方法。 (它位于同一个包这里的示例的缘故)

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) 
} 

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

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

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

这让我很想知道,是否需要审核我将要使用的每个包的全部代码库,包括所有的包依赖关系以及?仅仅因为我没有办法防止在一些外部软件包中错过恢复,这会损坏我的整个服务器并破坏我的用户体验?

还是有一些策略我不知道,当库代码中发生异步恐慌时,我可以优雅地失败吗?

我注意到从1.8开始有优雅的关机,但我不能使用它,因为我的程序已经崩溃。 https://golang.org/pkg/net/http/#Server.Shutdown

有大猩猩恢复处理程序,但同样,这只能防止同步恐慌。 http://www.gorillatoolkit.org/pkg/handlers#RecoveryHandler

更新:
我知道,恐慌是没有例外。重申这一点并不能回答这个问题,恐慌和例外并不是这个问题的关键。这个问题是关于理解语言可以提供什么工具来强制执行边界,而不需要读取整个包树中的每一行到开发人员。如果在语言中不可能,那么说明这是一个有效的答案。我只是不知道它是否是。

+0

总之,你没有。意想不到的恐慌会导致应用程序崩溃,并且预期的过程是重新启动它。 – JimB

恐慌并非例外。不要把它们当作例外,你会没事的。

首先第一件事情:包的API不应该恐慌,他们应该总是返回一个错误除了在某些非常罕见的情况下,然后他们必须清楚地记录何时以及为什么他们可以恐慌(regexp.MustCompile就是一个很好的例子可能恐慌的东西)。如果遇到错误(并且没有很好的理由这样做)的任何软件包都很糟糕,请不要使用它。

如果你做边界检查,确保不要访问零指针等,你不应该担心恐慌。

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

如果从第三方库中找到goroutine,请不要使用该库!如果他们不够严格,无法检查边缘案例和/或是否足够懒,以致出现错误,为什么您要使用他们的代码?谁知道它拥有哪些其他地雷?

如果goroutine是您自己的代码,请尝试消除可能导致恐慌的事情,然后添加恢复处理程序以捕获您无法防止的问题(如果需要)。

+0

所以你说,是的,你必须审核每个包,包括每个子包?这种语言似乎没有提供任何保证,一个包裹不会惊慌。 –

+3

如果您使用粗略的软件包,可能写得不好,那么是的。无论您使用何种语言编写,您都需要对它们进行审核。在真实世界的使用中,如果您使用的是高质量的热门软件包,我认为您会发现这比您想象的要少得多。 –

+0

一个典型的项目有10-20个依赖关系,每个依赖关系可能有5-10个依赖关系。我们正在谈论10万行开源代码。现在我有一位客户问我是否可以保证服务器不会随机崩溃并切断所有用户,使用nodejs我可以说是。当我的项目使用Go时,我该回答什么?这就是这个问题所关心的问题,任何回避都是令人分心的。对于中型服务器端项目,可以继续使用它自己的可靠吗? –