¿Puedo configurar max_retries para request.request?

Resuelto Kirill Zaitsev asked hace 11 años • 6 respuestas

El módulo de solicitudes de Python es simple y elegante, pero hay una cosa que me molesta. Es posible obtener un request.exception.ConnectionError con un mensaje como:

Max retries exceeded with url: ...

Esto implica que las solicitudes pueden intentar acceder a los datos varias veces. Pero no hay una sola mención de esta posibilidad en ninguna parte de los documentos. Al mirar el código fuente, no encontré ningún lugar donde pudiera alterar el valor predeterminado (presumiblemente 0).

Entonces, ¿es posible establecer de alguna manera el número máximo de reintentos para las solicitudes?

Kirill Zaitsev avatar Mar 15 '13 18:03 Kirill Zaitsev
Aceptado

Esto no solo cambiará max_retries sino que también habilitará una estrategia de retroceso que hace que las solicitudes a todas las direcciones http:// duerman durante un período de tiempo antes de volver a intentarlo (hasta un total de 5 veces):

import requests

from requests.adapters import HTTPAdapter, Retry

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')

Según la documentación paraRetry : si backoff_factor es 0,1 , entonces sleep() dormirá durante [0,05 s, 0,1 s, 0,2 s, 0,4 s, ...] entre reintentos. También forzará un reintento si el código de estado devuelto es 500 , 502 , 503 o 504 .

Varias otras opciones para Retrypermitir un control más granular:

  • total : número total de reintentos a permitir.
  • conectar : ​​cuántos errores relacionados con la conexión volver a intentar.
  • leer : cuántas veces reintentar en caso de errores de lectura.
  • redirección : cuántas redirecciones realizar.
  • Method_whitelist : conjunto de verbos de métodos HTTP en mayúsculas que deberíamos volver a intentar.
  • status_forcelist : un conjunto de códigos de estado HTTP que debemos forzar un reintento.
  • backoff_factor : un factor de retroceso que se aplicará entre intentos.
  • rise_on_redirect : si, si se agota el número de redirecciones, generar un correo electrónico MaxRetryErroro devolver una respuesta con un código de respuesta en el rango 3xx .
  • rise_on_status: significado similar a rise_on_redirect : si debemos generar una excepción o devolver una respuesta, si el estado cae en el rango status_forcelist y se han agotado los reintentos.

NB : rise_on_status es relativamente nuevo y aún no se ha incluido en una versión de urllib3 ni en solicitudes. El argumento de la palabra clave rise_on_status parece haber llegado a la biblioteca estándar como máximo en la versión 3.6 de Python.

Para hacer que las solicitudes se vuelvan a intentar con códigos de estado HTTP específicos, use status_forcelist . Por ejemplo, status_forcelist=[503] volverá a intentarlo con el código de estado 503 (servicio no disponible).

De forma predeterminada, el reintento solo se activa en estas condiciones:

  • No se pudo obtener una conexión desde el grupo.
  • TimeoutError
  • HTTPExceptionplanteado (de http.client en Python 3 o httplib ). Parecen ser excepciones HTTP de bajo nivel, como una URL o un protocolo no formado correctamente.
  • SocketError
  • ProtocolError

Tenga en cuenta que todas estas son excepciones que impiden recibir una respuesta HTTP normal. Si se genera alguna respuesta regular, no se realiza ningún reintento. Sin utilizar status_forcelist , ni siquiera se volverá a intentar una respuesta con estado 500.

Para que se comporte de una manera más intuitiva para trabajar con una API remota o un servidor web, usaría el fragmento de código anterior, que fuerza los reintentos en los estados 500 , 502 , 503 y 504 , todos los cuales no son infrecuentes en el web y (posiblemente) recuperable dado un período de espera lo suficientemente grande.

datashaman avatar Feb 19 '2016 11:02 datashaman

Es la urllib3biblioteca subyacente la que realiza el reintento. Para establecer un recuento máximo de reintentos diferente, utilice adaptadores de transporte alternativos :

from requests.adapters import HTTPAdapter

s = requests.Session()
s.mount('http://stackoverflow.com', HTTPAdapter(max_retries=5))

El max_retriesargumento toma un número entero o un Retry()objeto ; este último le brinda un control detallado sobre qué tipos de fallas se reintentan (un valor entero se convierte en una Retry()instancia que solo maneja fallas de conexión; los errores después de establecer una conexión no se manejan de manera predeterminada, ya que podrían generar efectos secundarios) .


Respuesta anterior, anterior al lanzamiento de las solicitudes 1.2.1 :

La requestsbiblioteca realmente no hace que esto sea configurable, ni tiene la intención de hacerlo (consulte esta solicitud de extracción ). Actualmente (solicitudes 1.1), el recuento de reintentos está establecido en 0. Si realmente desea establecerlo en un valor más alto, deberá configurarlo globalmente:

import requests

requests.adapters.DEFAULT_RETRIES = 5

Esta constante no está documentada; Úselo bajo su propia responsabilidad, ya que versiones futuras podrían cambiar la forma en que se maneja esto.

Actualización : y esto cambió ; en la versión 1.2.1 se agregó la opción para configurar el max_retriesparámetro en la HTTPAdapter()clase , por lo que ahora debe usar adaptadores de transporte alternativos, ver arriba. El enfoque de parche de mono ya no funciona, a menos que también parchee los HTTPAdapter.__init__()valores predeterminados (no es muy recomendable).

Martijn Pieters avatar Mar 15 '2013 11:03 Martijn Pieters

Tenga cuidado, la respuesta de Martijn Pieters no es adecuada para la versión 1.2.1+. No puede configurarlo globalmente sin parchear la biblioteca.

Puedes hacer esto en su lugar:

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))
gizmondo avatar Aug 12 '2013 15:08 gizmondo

Después de luchar un poco con algunas de las respuestas aquí, encontré una biblioteca llamada backoff que funcionó mejor para mi situación. Un ejemplo básico:

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()

Aún así recomendaría darle una oportunidad a la funcionalidad nativa de la biblioteca, pero si tiene algún problema o necesita un control más amplio, retroceder es una opción.

Brad Koch avatar Nov 06 '2017 21:11 Brad Koch

Puede utilizar la biblioteca de solicitudes para realizar todo de una sola vez. El siguiente código se reintentará 3 veces si recibe el código de estado 429,500,502,503 o 504, cada vez con un retraso mayor establecido a través de "backoff_factor". Consulte https://findwork.dev/blog/advanced-usage-python-requests-timeouts-retries-hooks/ para obtener un buen tutorial.

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

retry_strategy = Retry(
    total=3,
    backoff_factor=1,
    status_forcelist=[429, 500, 502, 503, 504],
    method_whitelist=["HEAD", "GET", "OPTIONS"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
http = requests.Session()
http.mount("https://", adapter)
http.mount("http://", adapter)

response = http.get("https://en.wikipedia.org/w/api.php")
Moses avatar Dec 13 '2021 14:12 Moses