Могу ли я установить max_retries для request.request?
Модуль запросов Python прост и элегантен, но меня беспокоит одна вещь.
Можно получить request.exception.ConnectionError с сообщением типа:
Max retries exceeded with url: ...
Это означает, что запросы могут пытаться получить доступ к данным несколько раз. Но нигде в документах не упоминается об этой возможности. Если посмотреть на исходный код, я не нашел места, где я мог бы изменить значение по умолчанию (предположительно 0).
Так можно ли как-то установить максимальное количество попыток для запросов?
Ответы
Ответ 1
Это базовая библиотека urllib3
, которая выполняет повторную попытку. Чтобы установить другой максимальный счетчик повторов, используйте альтернативные транспортные адаптеры:
from requests.adapters import HTTPAdapter
s = requests.Session()
s.mount('http://stackoverflow.com', HTTPAdapter(max_retries=5))
Аргумент max_retries
принимает целое число или объект Retry()
; последний дает вам мелкомасштабный контроль над тем, какие виды сбоев повторяются (целочисленное значение превращается в экземпляр Retry()
, который обрабатывает только сбои соединения; ошибки после подключения выполняются по умолчанию, не обрабатываются, так как это может привести к сбою -effects).
Старый ответ, предшествующий выпуску запросов 1.2.1:
Библиотека requests
на самом деле не делает эту конфигурацию и не намеревается (см. этот запрос на перенос). В настоящее время (запросы 1.1) счетчик попыток устанавливается равным 0. Если вы действительно хотите установить его на более высокое значение, вам нужно установить это глобально:
import requests
requests.adapters.DEFAULT_RETRIES = 5
Эта константа не документирована; используйте его на свой страх и риск, поскольку будущие выпуски могут изменить способ обработки.
Обновить: и это изменилось; в версии 1.2.1 возможность установить параметр max_retries
на HTTPAdapter()
class, так что теперь вам нужно использовать альтернативные транспортные адаптеры, см. выше. Подход обезьяны-патча больше не работает, если вы также не исправляете значения по умолчанию HTTPAdapter.__init__()
(очень не рекомендуется).
Ответ 2
Это не только изменит max_retries, но также включит стратегию отката, которая переводит запросы ко всем адресам http://на некоторое время перед повторной попыткой (в общей сложности 5 раз):
import requests
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
s = requests.Session()
retries = Retry(total=5,
backoff_factor=0.1,
status_forcelist=[ 500, 502, 503, 504 ])
s.mount('http://', HTTPAdapter(max_retries=retries))
s.get('http://httpstat.us/500')
Согласно документации для Retry
: если backoff_factor равен 0,1, то sleep() будет бездействовать в течение [0.1s, 0.2s, 0.4s,...] между повторными попытками. Также будет произведена повторная попытка, если возвращен код состояния 500, 502, 503 или 504.
Различные другие варианты Retry
позволяют более детальный контроль:
- итого - общее количество повторных попыток.
- connect - Сколько ошибок, связанных с подключением, нужно повторить.
- read - сколько раз повторить попытку чтения.
- redirect - сколько перенаправлений выполнить.
- method_whitelist - Набор прописных глаголов метода HTTP, к которым мы должны повторить попытку.
- status_forcelist - набор кодов состояния HTTP, которые мы должны принудительно повторить.
- backoff_factor - Коэффициент отката, применяемый между попытками.
- подъем_он_редакта - следует ли, если количество перенаправлений исчерпано, вызвать
MaxRetryError
или вернуть ответ с кодом ответа в диапазоне 3xx. - повышение_он_статуса - значение, аналогичное повышению_он_редакта: следует ли нам вызывать исключение или возвращать ответ, если состояние падает в диапазоне состояния_соглашения и повторные попытки были исчерпаны.
NB: повышение_он_стата является относительно новым и еще не превратило его в выпуск urllib3 или запросов. Похоже, что аргумент ключевого слова rise_on_status вошел в стандартную библиотеку максимум в версии Python 3.6.
Чтобы повторять запросы на определенные коды состояния HTTP, используйте status_forcelist. Например, status_forcelist = [503] будет повторять попытку с кодом состояния 503 (услуга недоступна).
По умолчанию повтор запускается только для следующих условий:
- Не удалось получить соединение из пула.
-
TimeoutError
-
HTTPException
(из http.client в Python 3 или httplib). Похоже, что это низкоуровневые исключения HTTP, такие как URL или протокол сформированы неправильно. -
SocketError
-
ProtocolError
Обратите внимание, что все это исключения, которые препятствуют получению регулярного ответа HTTP. Если генерируется какой-либо регулярный ответ, повтор не выполняется. Без использования status_forcelist, даже ответ со статусом 500 не будет повторен.
Чтобы заставить его вести себя более интуитивно для работы с удаленным API или веб-сервером, я бы использовал приведенный выше фрагмент кода, который заставляет повторять попытки для состояний 500, 502, 503 и 504, которые все нередки на сеть и (возможно) восстанавливаемый, учитывая достаточно большой период отсрочки.
РЕДАКТИРОВАНИЕ: Импорт класса Retry
напрямую из urllib3.
Ответ 3
Будьте осторожны, ответ Martijn Pieters не подходит для версии 1.2.1+. Вы не можете установить его глобально, не исправляя библиотеку.
Вместо этого вы можете сделать это:
import requests
from requests.adapters import HTTPAdapter
s = requests.Session()
s.mount('http://www.github.com', HTTPAdapter(max_retries=5))
s.mount('https://www.github.com', HTTPAdapter(max_retries=5))
Ответ 4
Немного поразмыслив с некоторыми из ответов, я нашел библиотеку под названием backoff, которая лучше подойдет для моей ситуации. Основной пример:
import backoff
@backoff.on_exception(
backoff.expo,
requests.exceptions.RequestException,
max_tries=5,
giveup=lambda e: e.response is not None and e.response.status_code < 500
)
def publish(self, data):
r = requests.post(url, timeout=10, json=data)
r.raise_for_status()
Я бы по-прежнему рекомендовал дать возможность нативной функциональности библиотеки, но если у вас возникнут какие-либо проблемы или вам понадобится более широкий контроль, можно воспользоваться откатом.
Ответ 5
Более чистый способ получить более высокий контроль может заключаться в том, чтобы упаковать вещи повтора в функцию и сделать эту функцию возвращенной с помощью декоратора и исключить белые списки исключений.
Я создал то же самое здесь:
http://www.praddy.in/retry-decorator-whitelisted-exceptions/
Воспроизведение кода в этой ссылке:
def retry(exceptions, delay=0, times=2):
"""
A decorator for retrying a function call with a specified delay in case of a set of exceptions
Parameter List
-------------
:param exceptions: A tuple of all exceptions that need to be caught for retry
e.g. retry(exception_list = (Timeout, Readtimeout))
:param delay: Amount of delay (seconds) needed between successive retries.
:param times: no of times the function should be retried
"""
def outer_wrapper(function):
@functools.wraps(function)
def inner_wrapper(*args, **kwargs):
final_excep = None
for counter in xrange(times):
if counter > 0:
time.sleep(delay)
final_excep = None
try:
value = function(*args, **kwargs)
return value
except (exceptions) as e:
final_excep = e
pass #or log it
if final_excep is not None:
raise final_excep
return inner_wrapper
return outer_wrapper
@retry(exceptions=(TimeoutError, ConnectTimeoutError), delay=0, times=3)
def call_api():
Ответ 6
while page is None:
try:
page = requests.get(url, timeout=5,proxies=proxies)
except Exception:
page = None