Зачем нужны тайм-ауты в HTTP запросах и как их подобрать
Что проверяют
Знаете ли вы, что в Go по умолчанию тайм-аутов нет, и умеете ли выбирать значения по данным, а не наугад. Следом обычно спрашивают, как тайм-аут связан с ретраями и с context входящего запроса.
Как отвечать
Зачем они нужны: без тайм-аута зависший сосед держит горутину, соединение и память, запросы копятся, и падает уже ваш сервис. Тайм-аут превращает зависание в быструю ошибку, которую можно обработать.
На клиенте:
http.Client.Timeout: общий лимит на весь обмен, включая чтение тела ответа. Уhttp.DefaultClientон нулевой, то есть бесконечный, поэтому в проде нужен свой клиент;http.Transport: лимиты отдельных этапов, напримерDialContextчерезnet.Dialer{Timeout: ...},TLSHandshakeTimeout,ResponseHeaderTimeout,IdleConnTimeout;context.WithTimeoutиhttp.NewRequestWithContext: дедлайн на конкретный вызов. Отмена родительского контекста прерывает и запрос.
На сервере http.ListenAndServe тоже работает без лимитов, и медленный клиент может держать соединения бесконечно (атака slowloris). Нужен свой http.Server с ReadHeaderTimeout, ReadTimeout, WriteTimeout, IdleTimeout, а для отдельного хендлера есть http.TimeoutHandler.
Подбирают по метрикам, а не по среднему. Берут p99 времени ответа зависимости и добавляют запас, затем сверяют с бюджетом всего запроса: если ваш SLA одна секунда, а вы зовёте три сервиса по очереди, секунды на каждый нет. Тайм-аут на попытку и общий дедлайн с ретраями считают отдельно, а ретраи делают с экспоненциальной задержкой и джиттером.
Где спотыкаются
Используют http.Get и дефолтный клиент в проде. Одна зависшая зависимость, и горутины копятся до исчерпания памяти.
Ставят тайм-аут вызова больше, чем дедлайн входящего запроса. Клиент уже ушёл, а сервис продолжает ждать ответа, который никому не нужен. Контекст входящего запроса надо прокидывать дальше.
Повторяют по тайм-ауту неидемпотентные запросы. Тайм-аут не значит, что операция не выполнилась: POST на списание мог пройти, и без ключа идемпотентности ретрай спишет деньги второй раз.