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

Как ищешь проблему, если получаешь в логах таймаут?

1.7 Middle🔥 231 комментариев
#Observability

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

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

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

Поиск причины таймаута в логах Go-приложения

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

1. Первичный анализ контекста

Сначала определяю тип таймаута и его контекст:

// Пример лога с таймаутом, на который обращаю внимание
// "context deadline exceeded" или "i/o timeout"
// "dial timeout" vs "read timeout" vs "write timeout"

Ключевые вопросы:

  • В какой операции произошел таймаут (HTTP-запрос, БД, gRPC, дисковый ввод-вывод)?
  • Распределенный ли это таймаут или локальный?
  • Есть ли паттерн (повторяется в определенное время, на определенных эндпоинтах)?
  • Каков уровень ошибок (одиночные случаи vs массовые)?

2. Иерархия проверок: от приложения к инфраструктуре

Уровень приложения:

// Проверяю конфигурацию таймаутов
httpClient := &http.Client{
    Timeout: 30 * time.Second, // Общий таймаут
    Transport: &http.Transport{
        DialContext: (&net.Dialer{
            Timeout:   5 * time.Second, // Таймаут установки соединения
        }).DialContext,
        TLSHandshakeTimeout:   5 * time.Second,
        ResponseHeaderTimeout: 10 * time.Second, // Таймаут заголовков
        IdleConnTimeout:       90 * time.Second,
    },
}

Что проверяю:

  • Корректность context.WithTimeout или context.WithDeadline
  • Настройки пулов соединений (слишком маленький pool может вызывать ожидание)
  • Блокирующие операции (каналы без select, мьютексы, блокировки GC)
  • Утечки горутин, которые могут "висеть" и не освобождать ресурсы

Уровень ресурсов:

# Мониторинг потребления ресурсов
go tool pprof http://localhost:6060/debug/pprof/goroutine
go tool trace trace.out
  • Профилирование CPU и памяти: pprof для поиска "горячих" мест
  • Анализ горутин: debug/pprof/goroutine?debug=2 для выявления заблокированных
  • Трассировка: go tool trace для визуализации выполнения

Уровень сетевого стека:

// Включаю детальное логирование HTTP-транспорта при необходимости
transport := &http.Transport{
    Proxy: http.ProxyFromEnvironment,
    DialContext: (&net.Dialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
        DualStack: true,
    }).DialContext,
    MaxIdleConns:        100,
    MaxIdleConnsPerHost: 10,
}

3. Диагностические техники

A. Изоляция проблемы:

  1. Воспроизведение в staging-окружении
  2. Упрощение сценария до минимального воспроизводящего примера
  3. A/B-тестирование изменений конфигурации

B. Инструменты мониторинга:

  • Prometheus/Grafana для трекинга latency, error rate, saturation
  • 分布式追踪 (Jaeger, OpenTelemetry) для анализа полного пути запроса
  • Логи с трейс-идентификаторами для корреляции событий

C. Анализ зависимостей:

// Проверяю health check зависимостей
resp, err := healthClient.Get("http://dependency-service/health")
if err != nil || resp.StatusCode != 200 {
    // Логирую состояние зависимости
    metrics.DependencyFailure.WithLabelValues("db").Inc()
}

4. Типичные причины таймаутов в Go

Распространенные сценарии:

  1. Блокировки в коде:
// Проблемный код с блокировкой
var globalMu sync.Mutex

func Process() {
    globalMu.Lock()         // Если эта блокировка удерживается долго
    defer globalMu.Unlock() // другие горутины будут ждать
    // Долгая операция
}
  1. Утечки горутин:
// Горутина, которая никогда не завершается при ошибках
go func() {
    for {
        select {
        case <-ctx.Done():
            return
        case data := <-ch:
            // Обработка, которая может "зависнуть"
        }
    }
}()
  1. Проблемы с GC (особенно при работе с большими структурами данных)
  2. Сетевые проблемы (DNS lookup timeout, packet loss)
  3. Неправильная настройка таймаутов (клиент vs серверные таймауты)

5. Проактивные практики предотвращения

  • Circuit Breaker паттерн: Использую github.com/sony/gobreaker для изоляции сбоев
  • Ретри с экспоненциальной отсрочкой: github.com/cenkalti/backoff/v4
  • Эталонные тесты производительности: go test -bench . -benchmem
  • Лимитирование запросов: golang.org/x/time/rate
  • Детализированное логирование с structured logging (zap, logrus)

6. Чеклист при инциденте

[ ] 1. Определил scope проблемы (один сервис/все)
[ ] 2. Проверил метрики ресурсов (CPU, memory, goroutine count)
[ ] 3. Проанализировал логи соседних сервисов
[ ] 4. Изучил изменения (деплои, конфигурации, трафик)
[ ] 5. Применил диагностические инструменты (pprof, trace)
[ ] 6. Упростил сценарий для воспроизведения
[ ] 7. Проверил health check зависимостей

Заключение: Поиск причин таймаута — это всегда процесс исключения. Начинаю с простого (логи, метрики), перехожу к сложному (профилирование, трассировка). В Go особенно важно проверять конкурентность и управление памятью, так как эти аспекты чаще всего становятся скрытыми причинами таймаутов. Системный подход и глубокое понимание стека технологий — ключ к эффективному решению таких проблем.

Как ищешь проблему, если получаешь в логах таймаут? | PrepBro