Ответ
Для отслеживания и анализа медленных запросов в 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, памяти, а также находить узкие места, вызывающие задержки.
Как использовать:
- Импортируйте пакет
net/http/pprof
. - Запустите 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")
}