requests.request에 대해 max_retries를 설정할 수 있습니까?


181

파이썬 요청 모듈은 간단하고 우아하지만 한 가지 버그가 있습니다. 다음 과 같은 메시지 로 requests.exception.ConnectionError 를 얻을 수 있습니다 .

Max retries exceeded with url: ...

이는 요청이 데이터에 여러 번 액세스하려고 시도 할 수 있음을 의미합니다. 그러나 문서의 어느 곳에서 나이 가능성에 대한 언급은 없습니다. 소스 코드를 보면 기본 (아마도 0) 값을 변경할 수있는 곳을 찾지 못했습니다.

그렇다면 요청에 대한 최대 재시도 횟수를 어떻게 설정할 수 있습니까?


9
2.x의 요청으로 이것에 대한 업데이트가 있습니까? requests.get (url, max_retries = num_max_retries)) 구현을 좋아합니다.
paragbaxi

11
@paragbaxi : 그리고 훨씬 더 나은requests.get(url, max_retries=num_max_retries, dely_between_retries=3))
WoJ

1
에) @WoJ 나는 당신의 예를 가져다가 그 현실을 만든 just.getjust.postgithub.com/kootenpv/just
PascalVKooten

2
요청이있는 재 시도에 관한 유용한 기사 : peterbe.com/plog/best-practice-with-retryries-with-requests
Gokul

답변:


161

urllib3재 시도를 수행 하는 기본 라이브러리입니다. 다른 최대 재시도 횟수를 설정하려면 대체 전송 어댑터를 사용하십시오 .

from requests.adapters import HTTPAdapter

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

max_retries인수 정수 또는 얻어 Retry()개체 ; 후자는 어떤 종류의 실패가 재 시도되는지에 대한 세밀한 제어를 제공합니다 (정수 값은 Retry()연결 실패 만 처리 하는 인스턴스로 바뀝니다. 연결된 후의 오류는 기본적으로 처리되지 않으므로 부작용이 발생할 수 있습니다) .


요청의 릴리스를 앞두고 이전 답변 1.2.1 :

requests라이브러리는 정말이 구성을,도 아니다 (참조 의도하지 않는 이 끌어 오기 요청 ). 현재 (요청 1.1) 재시도 횟수는 0으로 설정되어 있습니다. 실제로 더 높은 값으로 설정하려면 전역으로 설정해야합니다.

import requests

requests.adapters.DEFAULT_RETRIES = 5

이 상수는 문서화되어 있지 않습니다. 향후 릴리스에서는이 처리 방식이 변경 될 수 있으므로 위험에 따라 사용하십시오.

업데이트 : 이것은 변화; 버전 1.2.1에서 설정된 옵션 max_retries매개 변수 상의 HTTPAdapter()클래스는 이제 다른 전송 어댑터를 사용해야 할 정도로, 추가되었다, 위의 내용 참조. HTTPAdapter.__init__()기본값을 패치하지 않으면 원숭이 패치 방식이 더 이상 작동하지 않습니다 (매우 권장하지 않음).


9
필요하지 않은 경우 모든 사이트에 대해이를 지정할 필요는 없습니다. session.mount('http://', HTTPAdapter(max_retries=10))모든 http 연결 에서이 작업을 수행 할 수 있습니다 . https와 동일하게 모든 https 연결에 대해 작동합니다.
user136036

1
@ user136036 : 예, 가장 긴 접두사 일치로 어댑터를 찾습니다. 이것이 모든 URL 에 적용되기를 원 http://하며 https://사용할 수있는 최소 접두사 인 경우 답변이 연결되는 설명서를 참조하십시오.
Martijn Pieters

1
참고 HTTPAdapter(max_retries=5)특정 시나리오에 대해 작동합니다. 에서 요청 문서 , Note, this applies only to failed DNS lookups, socket connections and connection timeouts, never to requests where data has made it to the server. By default, Requests does not retry failed connections.어떤 상태 코드에 대한 강제 재 시도, 아래 datashaman의 답변을 @ 참조하십시오.
Steven Xu

@StevenXu : 예, Retry()재 시도 할 장애 시나리오를 변경 하도록 구성 할 수 있습니다 .
Martijn Pieters

225

이로 인해 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 후 슬립 () 잠 것 [0.1 초, 0.2 초, 0.4s, ...] 트라이 사이. 반환 된 상태 코드가 500 , 502 , 503 또는 504 인 경우에도 재 시도를 강제합니다 .

Retry보다 세밀한 제어 를 허용하는 다양한 기타 옵션 :

  • total – 허용되는 총 재시도 횟수입니다.
  • connect – 재 시도 할 연결 관련 오류 수.
  • 읽기 – 읽기 오류시 재시도 횟수입니다.
  • 리디렉션 – 수행 할 리디렉션 수입니다.
  • method_whitelist – 다시 시도해야하는 대문자 HTTP 메소드 동사 세트입니다.
  • status_forcelist – 강제로 다시 시도해야하는 일련의 HTTP 상태 코드입니다.
  • backoff_factor – 시도 사이에 적용 할 백 오프 계수.
  • raise_on_redirect – 리디렉션 수가 소진되었는지, 3xx 범위 MaxRetryError의 응답 코드를 사용하여 응답을 반환 할지 여부를 지정 합니다.
  • raise_on_status - 유사 의미 raise_on_redirect : 우리는 예외를 발생, 또는 상태에 빠진다 경우, 응답을 반환할지 여부를 status_forcelist 범위와 시도가 고갈되고있다.

NB : raise_on_status 는 비교적 새롭고 아직 urllib3 또는 요청의 릴리스로 만들지 않았습니다. raise_on_status 키워드 인수는 파이썬 버전 3.6에서 대부분의 표준 라이브러리에 그것을 만든 것으로 보인다.

특정 HTTP 상태 코드에서 요청을 재 시도하려면 status_forcelist를 사용 하십시오 . 예를 들어 status_forcelist = [503] 은 상태 코드 503 (서비스를 사용할 수 없음) 에서 다시 시도합니다 .

기본적으로 재 시도는 다음 조건에서만 실행됩니다.

  • 수영장에서 연결할 수 없습니다.
  • TimeoutError
  • HTTPException제기되었습니다 ( Python 3의 http.client 에서 그렇지 않으면 httplib ). URL 또는 프로토콜이 올바르게 구성되지 않은 것과 같은 저수준 HTTP 예외 인 것 같습니다.
  • SocketError
  • ProtocolError

이는 일반적인 HTTP 응답을 수신하지 못하게하는 모든 예외입니다. 경우 어떤 일정한 응답이 생성되고, 더 재 시도가 수행되지 않습니다. status_forcelist를 사용하지 않으면 상태 500의 응답조차도 재 시도되지 않습니다.

원격 API 또는 웹 서버 작업에보다 직관적 인 방식으로 작동하게하려면 위 코드 스 니펫을 사용하여 상태 500 , 502 , 503504 에서 재 시도를 강제 로 수행합니다. 충분한 백 오프 기간이 주어지면 웹 및 복구 가능합니다.

편집 : urllib3Retry 에서 직접 클래스를 가져 옵니다 .


1
논리를 구현하려고하는데 res 상태가 503인데도 로그에 하나의 요청 만 표시되므로 작동하는지 여부를 알 수 없습니다. 재 시도가 작동하는지 어떻게 알 수 있습니까? 코드 참조 : pastebin.com/rty4bKTw
Danilo Oliveira

1
첨부 된 코드가 예상대로 작동합니다. 트릭은 status_forcelist 매개 변수입니다. 이것은 urllib3 패키지가 특정 상태 코드를 재 시도하도록 지시합니다. 코드 : pastebin.com/k2bFbH7Z
datashaman

1
urllib3은 상태 503이 기본적으로 예외라고 생각하지 않습니다.
datashaman

1
@Connor 아니오, 어댑터가 세션에 연결되어 있습니다.
datashaman

1
urlib3.Retry는 더 이상 요청의 일부가 아닙니다. 직접 가져와야합니다. 제안 된 편집
2390183

59

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

22
좋은 해결책이지만 재시도 사이에 지연이 없습니다. 당신이 시도 사이에 자고 싶다면, 당신은 자신의 롤을해야합니다.
nofinator

18

여기에 몇 가지 답변으로 약간의 어려움을 겪은 후 백 오프라 는 라이브러리 가 내 상황에 더 효과적 이라는 것을 알았습니다 . 기본 예 :

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

여전히 라이브러리의 기본 기능을 사용하는 것이 좋지만 문제가 발생하거나 광범위한 제어가 필요한 경우 백 오프가 옵션입니다.


1
훌륭한 도서관, 감사합니다! 이 이외의 다른 기능에이 기능이 필요 requests하므로 완벽하게 작동합니다!
데니스 골 로마 조프

3

더 높은 제어력을 얻는 더 확실한 방법은 재시도 항목을 함수로 패키지하고 데코레이터를 사용하여 해당 함수를 재 시도하고 예외를 허용하는 것입니다.

나는 여기에 똑같이 만들었습니다 : 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():
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.