파이썬 요청 모듈은 간단하고 우아하지만 한 가지 버그가 있습니다. 다음 과 같은 메시지 로 requests.exception.ConnectionError 를 얻을 수 있습니다 .
Max retries exceeded with url: ...
이는 요청이 데이터에 여러 번 액세스하려고 시도 할 수 있음을 의미합니다. 그러나 문서의 어느 곳에서 나이 가능성에 대한 언급은 없습니다. 소스 코드를 보면 기본 (아마도 0) 값을 변경할 수있는 곳을 찾지 못했습니다.
그렇다면 요청에 대한 최대 재시도 횟수를 어떻게 설정할 수 있습니까?
답변
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__()
기본값을 패치하지 않으면 원숭이 패치 방식이 더 이상 작동하지 않습니다 (매우 권장하지 않음).
답변
이로 인해 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 , 503 및 504 에서 재 시도를 강제 로 수행합니다. 충분한 백 오프 기간이 주어지면 웹 및 복구 가능합니다.
편집 : urllib3Retry
에서 직접 클래스를 가져 옵니다 .
답변
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))
답변
여기에 몇 가지 답변으로 약간의 어려움을 겪은 후 백 오프라 는 라이브러리 가 내 상황에 더 효과적 이라는 것을 알았습니다 . 기본 예 :
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()
여전히 라이브러리의 기본 기능을 사용하는 것이 좋지만 문제가 발생하거나 광범위한 제어가 필요한 경우 백 오프가 옵션입니다.
답변
더 높은 제어력을 얻는 더 확실한 방법은 재시도 항목을 함수로 패키지하고 데코레이터를 사용하여 해당 함수를 재 시도하고 예외를 허용하는 것입니다.
나는 여기에 똑같이 만들었습니다 :
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():