我正在学习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服务器可能仍在为其他客户端服务,它可能是维护websockets,而我可能还想在这里返回500错误。
来自nodejs,我习惯于uncaughtException
的概念,用于处理未捕获的同步异常和unhandledRejection
来处理未捕获的异步异常。这两个进程构造使开发人员可以选择立即崩溃程序(如果有意义),或者记录错误,返回正确的http代码,然后根据需要优雅地关闭。
在我的在线研究中,我发现很多资源都说,恐慌并不像例外,它们很不寻常,你不需要担心它们。但在编写代码时,似乎很容易引起恐慌。完全取决于开发人员,以确保他的图书馆不会出现恐慌,人为因素100%参与其中。
这让我想知道,我是否需要审核我将要使用的每个软件包的整个代码库,包括所有软件包依赖项?仅仅因为我没有办法防止在一些外部软件包中丢失恢复,这将破坏我的整个服务器并破坏我的用户体验?
或者是否有一些策略我不知道当库代码中发生异步混乱时我可以优雅地失败?
我注意到从1.8开始正常关机,但我不能使用它,因为我的程序已经崩溃了。 https://golang.org/pkg/net/http/#Server.Shutdown
有大猩猩恢复处理程序,但同样,这只能防止同步恐慌。 http://www.gorillatoolkit.org/pkg/handlers#RecoveryHandler
更新
我知道恐慌不是例外。重申不回答问题,恐慌和例外并不是这个问题的关键。这个问题是关于理解语言可以提供哪些工具来强制执行边界,而不需要将整个包树中的每一行读取到开发人员身上。如果在语言中不可能,那么说明这是一个有效的答案。我只是不知道是不是。
答案 0 :(得分:1)
恐慌不是例外。不要把它们视为例外,你会没事的。
首先要做的事情是:软件包API永远不应该出现恐慌,它们应该总是返回错误,除非在某些非常罕见的情况下,然后必须清楚地记录它们何时以及为何可能发生恐慌({ {1}}是一个可能恐慌的好例子。如果遇到错误而导致恐慌的任何软件包(并且没有非常这样做的理由)是不好的,请不要使用它。
如果你做了检查,请确保不要使用nil指针等,你不应该担心恐慌。
至于恢复goroutine中的恐慌,除非goroutine有自己的恢复处理程序,否则你不能。
如果来自第三方库的goroutine,请不要使用该库!如果它们不够宽松,不检查边缘情况和/或懒得足以让出现错误,你为什么要使用他们的代码呢?谁知道它拥有什么其他地雷?
如果goroutine是你自己的代码,试着消除可能引起恐慌的事情,然后添加一个恢复处理程序来捕获那些你无法阻止的代码。