当前位置: 首页 > 知识库问答 >
问题:

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

皮自明
2023-03-14

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

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

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

共有1个答案

东门晟
2023-03-14

恐慌不是例外。不要把他们当成例外,你会没事的。

首先要做的事情是:包API永远不应该发生恐慌,它们应该总是返回错误,除非在某些非常罕见的情况下,然后必须清楚地记录它们何时以及为什么会发生恐慌(regexp.mustcompile就是可能发生恐慌的一个很好的例子)。任何软件包如果遇到错误(而且没有很好的理由这样做)就会惊慌失措,这是不好的,不要使用它。

如果您进行边界检查,请确保不接受零指针等,您永远不必担心恐慌。

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

如果goroutine是您自己的代码,请尝试消除可能导致恐慌的内容,然后添加恢复处理程序,以便在需要时捕捉无法防止的内容。

 类似资料:
  • 问题内容: 我曾经认为,如果goroutine中的恐慌的调用者在恐慌之前完成,它将使其终止程序(延迟恢复没有任何帮助,因为此时还没有发生恐慌), 直到我尝试以下代码: 我发现无论调用者函数完成与否,如果goroutines开始恐慌,调用者的延迟恢复机制将无济于事。整个程序仍然无效。 所以为什么?理论上,调用者功能仍在运行。当出现紧急情况时,调用者的延迟功能应起作用(包括恢复)。 问题答案: 该规范

  • 我曾经认为goroutine中的恐慌会杀死程序,如果它的调用者在恐慌之前完成(延迟恢复没有任何帮助,因为在这一点上还没有恐慌发生), 直到我尝试下面的代码: 我发现无论调用方函数是否完成,如果goroutines开始恐慌,调用方的延迟恢复机制都没有帮助。整个程序还是死气沉沉的。

  • 我试图从我的程序中创建的go例程中捕捉崩溃/恐慌,以便将它们发送到我的崩溃错误报告服务器(如Sentry/Raygun) 例如, 做这件事的惯用方法是什么?

  • 问题内容: 在defer函数中,我想查看一次恢复调用是否会产生非nil值(不恢复) 可能吗? 问题答案: 那确切的事情是不可能的。您可能只想重新恐慌,就像在其他语言中重新引发异常一样。