Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Стратегия обработки паники в Go
В Go паника (panic) — это механизм для обработки исключительных ситуаций, которые делают дальнейшее выполнение программы невозможной. В отличие от исключений в других языках, паника в Go предназначена для фатальных ошибок, а не для обычного управления потоком. Обрабатывать панику нужно на уровне, где можно безопасно восстановить состояние приложения или корректно завершить операцию.
Уровни обработки паники
1. Уровень горутин (goroutine)
Основное правило: панику нужно восстанавливать в каждой горутине, особенно в тех, которые запускаются асинхронно. Если паника останется необработанной, она приведёт к аварийному завершению всей программы.
func worker() {
defer func() {
if r := recover(); r != nil {
log.Printf("Восстановлено в worker: %v", r)
}
}()
// Код, который может запаниковать
panic("критическая ошибка в worker")
}
func main() {
go worker()
time.Sleep(time.Second) // Даём время на выполнение горутины
}
2. Уровень HTTP-обработчиков
В веб-серверах паники нужно обрабатывать в middleware или на уровне обработчика запросов, чтобы один упавший запрос не повлиял на остальные.
func recoveryMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if r := recover(); r != nil {
log.Printf("HTTP паника восстановлена: %v", r)
http.Error(w, "Внутренняя ошибка сервера", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/panic", func(w http.ResponseWriter, r *http.Request) {
panic("искусственная паника в обработчике")
})
wrappedMux := recoveryMiddleware(mux)
http.ListenAndServe(":8080", wrappedMux)
}
3. Уровень долгоживущих процессов
Для сервисов, которые должны работать непрерывно (например, consumer'ы очередей), обработка паники обязательна:
func processQueue() {
defer func() {
if r := recover(); r != nil {
log.Printf("Восстановлена паника в processQueue: %v", r)
// Можно добавить логику перезапуска
time.Sleep(time.Second)
go processQueue() // Перезапуск
}
}()
for {
// Чтение и обработка сообщений
// Возможная точка паники
}
}
Где НЕ нужно обрабатывать панику
- Библиотеки и пакеты: оставляйте панику необработанной, чтобы вызывающий код мог решить, как её обработать. Библиотеки должны возвращать ошибки, а не паниковать, за исключением действительно фатальных ситуаций.
- Критические ошибки при инициализации: если при запуске приложения что-то пошло не так (например, не удалось подключиться к базе данных), часто логичнее дать программе упасть, чтобы проблема была сразу заметна.
Best Practices
- Используйте
recover()только вdeferфункциях — это единственное место, гдеrecover()работает. - Логируйте паники с максимальным контекстом для отладки.
- Не злоупотребляйте паникой — для ожидаемых ошибок используйте стандартный механизм возврата ошибок.
- Восстанавливайтесь осмысленно — после
recover()приложение должно остаться в консистентном состоянии.
func safeOperation() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("паника восстановлена: %v", r)
}
}()
// Опасная операция
dangerousCall()
return nil
}
Обработка паники — это страховочный механизм. Основной поток ошибок должен обрабатываться через возвращаемые значения error. Паника должна оставаться для действительно исключительных ситуаций, когда продолжение работы невозможно.