Короткий ответ
Нужно явно передавать параметр timeout в вызов requests.get/requests.post и оборачивать вызов в try/except, обрабатывая requests.exceptions.Timeout и requests.exceptions.RequestException отдельно от бизнес-логики успешного ответа.
Как это работает подробнее
без явного timeout запрос может "висеть" бесконечно, если внешний сервис не отвечает, занимая поток/соединение и потенциально истощая пул ресурсов приложения. timeout можно задать как одно число (на соединение и чтение вместе) или tuple (connect_timeout, read_timeout).
Пример кода
example.py
import requests
def call_external_service(order_id: int) -> dict | None:
try:
resp = requests.get(
f"https://partner.example.com/orders/{order_id}",
timeout=(2, 5), # 2с на соединение, 5с на чтение ответа
)
resp.raise_for_status()
return resp.json()
except requests.exceptions.Timeout:
log.warning("partner service timed out", order_id=order_id)
return None
except requests.exceptions.RequestException as exc:
log.error("partner service call failed", order_id=order_id, error=str(exc))
return NoneЧто использовать на практике
- Рекомендация. таймаут стоит выбирать исходя из SLA внешнего сервиса и допустимой задержки для вашего пользователя, а не ставить "на глаз" — слишком маленький таймаут даст ложные обрывы под нагрузкой, слишком большой — удержит ресурсы при реальном отказе внешнего сервиса. При таймауте нужно решить: вернуть пользователю ошибку, попробовать retry, или отдать fallback-данные — выбор зависит от критичности операции.
Подводные камни
- Риски. вызывать
requests.get(url)безtimeoutвообще — по умолчанию библиотека будет ждать бесконечно; ловить общийExceptionбез различения таймаута, ошибки соединения и ошибки HTTP-статуса, что мешает принимать разные решения для разных сбоев.