Как оправиться от асинхронной паники во внешнем пакете

Я изучаю 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 может по-прежнему обслуживать других клиентов, он может поддерживать веб-сокеты, и я также хотел бы вернуть здесь ошибку 500.

Исходя из nodejs, я привык к понятию uncaughtException, для обработки неоткрытых синхронных исключений и unhandledRejection обрабатывать незафиксированные асинхронные исключения. Эти две конструкции процесса дают разработчику возможность либо сразу завершить работу программы (если это имеет смысл), либо записать в журнал ошибку, вернуть правильный http-код, а затем, возможно, изящно завершить работу при необходимости.

В моих онлайн-исследованиях я нахожу много ресурсов, говорящих, что паника не похожа на исключения, они необычны, вам не нужно беспокоиться о них. Но кажется, что на самом деле очень легко вызвать панику при написании кода. Разработчик должен убедиться, что его библиотека не паникует, человеческий фактор задействован на 100%.

Это заставляет меня задуматься: нужно ли проверять всю кодовую базу каждого пакета, который я собираюсь использовать, включая все зависимости пакета? только потому, что у меня нет средств защиты от пропущенного восстановления в каком-то внешнем пакете, который разрушит весь мой сервер и испортит работу моего пользователя?

Или есть какая-то стратегия, о которой я не знаю, что я могу изящно потерпеть неудачу, когда в коде библиотеки возникает асинхронная паника?

Я заметил, что начиная с версии 1.8 происходит постепенное завершение работы, но я не могу использовать это, потому что моя программа уже потерпела крах. https://golang.org/pkg/net/http/

Есть обработчик восстановления гориллы, но опять же, это только защищает от синхронной паники. http://www.gorillatoolkit.org/pkg/handlers

Обновить:
Я осознаю, что паника не является исключением. Положение, которое не отвечает на вопрос, панику и исключения - не то, о чем этот вопрос. Этот вопрос касается понимания того, какие инструменты может предоставить язык для обеспечения соблюдения границ без необходимости читать каждую строку во всем дереве пакетов для разработчика. Если это невозможно на языке, то это правильный ответ. Я просто не знаю, так это или нет.

1 ответ

Паника не является исключением. Не относитесь к ним как к исключениям, и у вас все будет хорошо.

Перво-наперво: пакетные API никогда не должны паниковать, они всегда должны возвращать ошибку, за исключением некоторых очень редких случаев, а затем они должны быть четко задокументированы относительно того, когда и почему они могут паниковать (regexp.MustCompile хороший пример того, что может паниковать). Любой пакет, который паникует, если обнаруживает ошибку (и не имеет для этого веских причин), плох, не используйте его.

Если вы выполняете проверку границ, убедитесь, что не обращаетесь к нулевым указателям и т. Д., Вам никогда не придется беспокоиться о панике.

Что касается восстановления паники в goroutine, если у goroutine нет своего собственного обработчика восстановления, вы не можете.

Если программа была загружена из сторонней библиотеки, не используйте эту библиотеку! Если они достаточно слабые, чтобы не проверять крайние случаи и / или достаточно ленивы, чтобы просто паниковать при ошибке, почему вы используете их код? Кто знает, какие еще мины он держит?

Если программа является вашим собственным кодом, попробуйте устранить вещи, которые могут вызывать панику, а затем добавить обработчик восстановления, чтобы перехватить те, которые вы не можете предотвратить, если это необходимо.

Другие вопросы по тегам