Какие существуют таймауты для HTTP-запросов в Go и как их правильно настраивать?

Ответ

Правильная настройка таймаутов критически важна для создания надежных и предсказуемых HTTP-клиентов. В Go существует несколько уровней таймаутов, которые настраиваются в http.Client и http.Transport.

Основные виды таймаутов:


  1. http.Client.Timeout: Общий таймаут. Ограничивает общее время выполнения всего запроса, включая установку соединения, отправку запроса, чтение заголовков и тела ответа. Это самый простой и грубый способ установить ограничение.



  2. net.Dialer.Timeout: Таймаут на установку соединения. Ограничивает время, затрачиваемое на установку TCP-соединения с сервером. Настраивается внутри http.Transport.



  3. http.Transport.ResponseHeaderTimeout: Таймаут на чтение заголовков. Ограничивает время ожидания заголовков ответа после полной отправки запроса.



  4. http.Transport.TLSHandshakeTimeout: Таймаут на TLS-рукопожатие. Ограничивает время, затрачиваемое на установку безопасного TLS-соединения.



  5. http.Transport.IdleConnTimeout: Таймаут простоя соединения. Определяет, как долго неиспользуемое соединение будет оставаться открытым в пуле. Важно для управления ресурсами.


Пример детальной настройки:

client := &http.Client{
    // Общий таймаут на весь запрос
    Timeout: 30 * time.Second,
    Transport: &http.Transport{
        DialContext: (&net.Dialer{
            // Таймаут на установку TCP-соединения
            Timeout:   5 * time.Second,
        }).DialContext,
        // Таймаут на TLS-рукопожатие
        TLSHandshakeTimeout: 10 * time.Second,
        // Таймаут на получение заголовков ответа
        ResponseHeaderTimeout: 15 * time.Second,
        // Таймаут на 100-Continue от сервера
        ExpectContinueTimeout: 1 * time.Second,
    },
}

Рекомендации:

  • Всегда устанавливайте таймауты. Нулевое значение означает отсутствие таймаута, что может привести к вечной блокировке горутины.
  • Для внутренних сервисов (внутри одного дата-центра): обычно достаточно коротких таймаутов (1-5 секунд).
  • Для внешних API: таймауты должны быть больше (5-30 секунд), но всегда меньше, чем таймаут на стороне вашего клиента (например, таймаут HTTP-запроса от пользователя).
  • Баланс: Client.Timeout должен быть больше суммы остальных таймаутов, чтобы дать им сработать. Например: DialTimeout (5s) + ResponseHeaderTimeout (10s) < Client.Timeout (30s).