Повторы и таймауты в HTTP-клиенте: типичные ошибки
Повторный запрос может помочь пережить временный сбой, а может удвоить нагрузку и повторно списать деньги. Разбираем настройки HTTP-клиента Python Requests 2.x: какие таймауты задавать, что повторять и как не получить бесконечную очередь запросов.

Если внешний API отвечает медленно, повтор запроса кажется простым спасением. Но без таймаута каждый поток может зависнуть надолго, а без ограничений повторов несколько копий одного запроса способны усилить сбой. Ниже — практические настройки для Python Requests 2.x и urllib3 2.x: что повторять, с какой паузой и где автоматический повтор опасен.
Сначала разделите таймаут и повтор
Таймаут ограничивает ожидание ответа. Повтор запускает новый запрос после определённой ошибки. Это разные механизмы: в Requests нужно передать таймаут в сам запрос, а правила повторов настроить отдельно.
В Requests значение timeout может быть одним числом или парой значений: таймаутом соединения и таймаутом чтения. Пара вида (3.05, 10) не означает, что весь запрос гарантированно завершится за 13,05 секунды. Таймаут чтения ограничивает ожидание данных от сервера, а не общий срок операции. Если нужна строгая общая граница, её следует контролировать на уровне приложения или фоновой задачи.
Для примеров ниже нужны Python 3 и установленный Requests 2.x. Код использует urllib3 2.x, установленный как зависимость Requests. Перед обновлением зависимостей сверяйте правила Retry с документацией именно той версии urllib3, которая стоит в вашем окружении.
Настройте повторы только для подходящих запросов
У GET обычно нет побочного эффекта на сервере, поэтому повтор после временной сетевой ошибки может быть безопасен. У POST ситуация другая: сервер мог выполнить операцию, а ответ потерялся по дороге. Клиент увидит ошибку и отправит операцию второй раз. Так появляются двойные платежи, дубли заказов и повторная отправка форм.
В urllib3 2.x стратегию можно задать через Retry и подключить к Requests с помощью HTTPAdapter. Этот пример допускает повторы для GET и HEAD, ограничивает их числом попыток и задаёт экспоненциальную паузу:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util import Retry
retry_policy = Retry(
total=3,
connect=3,
read=0,
status=3,
allowed_methods=frozenset({"GET", "HEAD"}),
status_forcelist={429, 500, 502, 503, 504},
backoff_factor=0.5,
respect_retry_after_header=True,
)
session = requests.Session()
adapter = HTTPAdapter(max_retries=retry_policy)
session.mount("https://", adapter)
session.mount("http://", adapter)
response = session.get(
"https://api.example.com/items",
timeout=(3.05, 10),
)
response.raise_for_status()
Здесь total=3 означает до трёх повторов сверх первой попытки, если срабатывает соответствующее правило. read=0 намеренно запрещает автоматический повтор после ошибки чтения: сервер мог уже обработать запрос. connect=3 разрешает повтор при проблеме с установлением соединения. Для POST такую политику нельзя включать автоматически только потому, что API иногда отвечает ошибкой.
status_forcelist задаёт HTTP-статусы, после которых возможен повтор, а allowed_methods ограничивает его безопасными для этой конфигурации методами. Статус 429 означает, что сервер ограничивает частоту запросов: учитывайте заголовок Retry-After, если он пришёл, и не отправляйте запросы чаще в обход ограничения.
Сравните три подхода
| Подход | Что выигрываете | Чем платите |
|---|---|---|
| Таймаут без повтора | Понятный предел ожидания и отсутствие скрытых дополнительных запросов | Разовый сетевой сбой сразу виден вызывающему коду |
| Ограниченный повтор GET по статусам и сетевым ошибкам | Устойчивость к части временных сбоев без повторения типичной записи данных | Дополнительная задержка и нагрузка; важно правильно выбрать методы и статусы |
| Повтор POST с ключом идемпотентности | Можно безопаснее повторять операцию, если API гарантирует обработку ключа | Нужна поддержка со стороны API и правила хранения ключей; без них гарантий нет |
Идемпотентность1 — свойство операции, при котором повторное выполнение не меняет итог после первого успешного выполнения. Например, запрос «установить статус заказа в оплачен» может быть идемпотентным, а «списать 500 рублей» — нет, если сервер не защищает его от дублей.
Повтор — это не лечение ошибки, а ещё один запрос к системе, которая уже могла быть перегружена.
Где настройка чаще всего ломается
Таймаут забыли передать в запрос
Настройка повторов сама по себе не задаёт предел ожидания ответа. Если вызов может ждать внешнюю систему, укажите таймаут в каждом запросе или централизованно оберните вызовы своим клиентским слоем. Проверьте также, что таймауты не превышают дедлайн задачи или HTTP-запроса, который их вызвал.
Повторяют POST после любой ошибки
Сбой чтения не доказывает, что сервер не выполнил операцию. Для POST повтор безопасен только при подходящем контракте API: например, сервер поддерживает ключ идемпотентности и возвращает результат уже принятой операции при повторе того же ключа. Не придумывайте такой заголовок сами — уточните точное имя и поведение в документации конкретного API.
Слишком много попыток и слишком короткие паузы
Повторы увеличивают время ответа и нагрузку. Если приложение само повторяет запрос трижды, а библиотека внутри делает ещё три повтора, число обращений к серверу может оказаться выше ожидаемого. Выберите одно место, которое отвечает за повторы, посчитайте максимальное число попыток и проверьте, укладывается ли суммарное ожидание в бюджет времени операции.
Не проверяют фактический результат
Успешный HTTP-ответ не всегда означает успешную операцию: проверьте статус и формат ответа, а для API с отдельным полем результата — его значение. Ошибки нужно логировать с адресом сервиса, числом попыток и итоговым статусом, но без токенов, паролей и персональных данных.
Когда какой вариант выбирать
- Для чтения данных начните с таймаута и небольшого числа повторов для временных сетевых ошибок и ограниченного списка статусов.
- Для записи сначала выясните, как API предотвращает дубли. Если такой защиты нет, не включайте автоматический повтор POST при ошибках чтения.
- Для 429 соблюдайте указания сервера и ограничивайте частоту запросов; повтор без паузы может усугубить ограничение.
- Для длинных операций используйте очередь или фоновую задачу с общим дедлайном, а не бесконечное ожидание внутри пользовательского запроса.
Что делать дальше
Проверьте реальную конфигурацию зависимостей: версию Requests и urllib3, количество попыток во всех слоях и таймауты каждого исходящего вызова. Затем измерьте долю ошибок, число попыток на операцию и полное время ответа с повторами. Отдельно проверьте сценарий, когда сервер выполнил POST, но соединение оборвалось до получения ответа: именно он показывает, защищены ли ваши данные от дублей.
1 Идемпотентность не означает, что запрос можно повторять без ограничений: важны конкретный метод, контракт API и способ защиты от повторной обработки.
IT-архитектор / SQL-оптимизатор: Поиск N+1, оптимизация запросов, code review, поиск багов.
Спросить за 20 ₽Источники: Requests: таймауты, urllib3: Retry
Частые вопросы
Какой таймаут поставить для Requests?
Можно ли автоматически повторять POST?
Сколько запросов отправит Retry(total=3)?
Что делать при статусе 429?
Повтор после таймаута чтения безопасен?
Подойдут ли примеры для другой версии urllib3?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.