Надёжный HTTP-клиент в Go
Вопрос
Сервис часто ходит во внешние API. Как вы настроите HTTP-клиент, чтобы он был быстрым и надёжным?
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Один долгоживущий http.Client с общим Transport на сервис
Таймауты: Client.Timeout или ctx с дедлайном, плюс таймауты соединения, TLS и ResponseHeaderTimeout
Пул соединений: MaxIdleConns, MaxIdleConnsPerHost под параллелизм, IdleConnTimeout
Всегда закрывать Body и дочитывать его для переиспользования соединения
Ретраи только идемпотентных запросов с backoff и джиттером, circuit breaker
Проверка StatusCode, ограничение размера ответа, метрики и трейсинг
Что могут спросить следом
- Почему нельзя создавать новый Transport на каждый запрос?
- Как понять, что соединения не переиспользуются?
Разбор
Создаю один клиент на сервис и переиспользую: Transport внутри держит пул соединений, и новый Transport на каждый запрос означает новые TCP и TLS handshake и утечку пулов. Таймауты на всех уровнях: DialContext с таймаутом соединения, TLSHandshakeTimeout, ResponseHeaderTimeout, а общее время запроса через контекст с дедлайном от вызывающего. Пул: MaxIdleConnsPerHost поднимаю до ожидаемого параллелизма к одному хосту, по умолчанию там 2, и это частая причина TIME_WAIT и задержек. После ответа всегда defer resp.Body.Close(), а тело дочитываю или ограничиваю io.LimitReader. Сеть даёт ошибку в err, а 500 нет, поэтому проверяю StatusCode. Ретраи только для идемпотентных методов и ответов 502, 503, 504, с экспоненциальным backoff и джиттером, с бюджетом ретраев, плюс circuit breaker, чтобы не добивать лежащий сервис. Метрики по хосту и коду, трейсинг через otelhttp. Переиспользование соединений проверяю через httptrace (поле Reused) или по метрикам числа соединений.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Термины по теме
Ещё по теме «net/http и middleware»
- Статистика access-логов: ошибки и p95 на Go
- Разбор сырого HTTP/1.1 запроса на Go
- Разбор query-строки URL с декодированием на Go
- Маршрутизатор с параметрами пути в стиле ServeMux
- Rate limiter на токен-бакете для middleware на Go
- Зачем закрывать resp.Body
- Таймаут http.DefaultClient
- Обработчики net/http и горутины