Как отследить и проанализировать медленные запросы в Go?

Ответ

Для отслеживания и анализа медленных запросов в Go-приложении используется комбинация нескольких подходов:

1. Простое измерение времени (Middleware)

Самый простой способ — измерить время выполнения запроса вручную. В веб-сервисах это удобно делать с помощью middleware-функции, которая оборачивает основной обработчик.

func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        duration := time.Since(start)

        // Логируем, если запрос выполнялся дольше определенного порога
        if duration > 500*time.Millisecond {
            log.Printf("[%s] %s took %s (SLOW)", r.Method, r.RequestURI, duration)
        }
    })
}

// Использование:
// http.Handle("/", LoggingMiddleware(myHandler))

2. Профилирование с помощью pprof

pprof — это стандартный и мощный инструмент для профилирования Go-приложений. Он позволяет анализировать использование CPU, памяти, а также находить узкие места, вызывающие задержки.

Как использовать:

  1. Импортируйте пакет net/http/pprof.
  2. Запустите HTTP-сервер (часто на отдельном порту).
import (
    _ "net/http/pprof" // Анонимный импорт для регистрации обработчиков pprof
    "log"
    "net/http"
)

go func() {
    // Запускаем сервер pprof на порту 6060
    log.Println(http.ListenAndServe("localhost:6060", nil))
}()

После запуска вы можете использовать утилиту go tool pprof или веб-интерфейс (http://localhost:6060/debug/pprof/) для анализа:

  • CPU Profile: Показывает, какие функции потребляют больше всего процессорного времени.
  • Trace: go tool trace — особенно полезен для анализа задержек (latency). Он визуализирует выполнение горутин во времени и помогает найти, где программа "простаивала".

3. Распределенная трассировка (Distributed Tracing)

В микросервисной архитектуре, где запрос проходит через несколько сервисов, для отслеживания задержек используется распределенная трассировка.

  • Стандарт: OpenTelemetry является индустриальным стандартом.
  • Инструменты: Jaeger, Zipkin, Datadog.

Трассировка позволяет построить полную карту пути запроса через все сервисы и увидеть, какой из них внес наибольшую задержку.

4. Контекст с таймаутом (для контроля, а не отслеживания)

context.WithTimeout или context.WithDeadline не отслеживают медленные запросы, а предотвращают их, прерывая операцию по истечении времени. Это важный механизм для обеспечения отказоустойчивости системы.

// Устанавливаем дедлайн в 2 секунды
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

// Передаем контекст в долгую операцию (например, запрос в БД или другой сервис)
err := longOperation(ctx)

if errors.Is(err, context.DeadlineExceeded) {
    // Операция была прервана по таймауту
    log.Println("Request failed due to timeout")
}