← Назад к вопросам

Где нужно обрабатывать панику?

1.0 Junior🔥 151 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Стратегия обработки паники в 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

  1. Используйте recover() только в defer функциях — это единственное место, где recover() работает.
  2. Логируйте паники с максимальным контекстом для отладки.
  3. Не злоупотребляйте паникой — для ожидаемых ошибок используйте стандартный механизм возврата ошибок.
  4. Восстанавливайтесь осмысленно — после recover() приложение должно остаться в консистентном состоянии.
func safeOperation() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("паника восстановлена: %v", r)
        }
    }()
    
    // Опасная операция
    dangerousCall()
    return nil
}

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

Где нужно обрабатывать панику? | PrepBro