아주 단순하게 문제를 해결하자면 루프 구조 내에서 원하는 결과가 나올 때까지 응답을 계속하는 방법이 있을 수 있다.
# 방법 1 def get(url): try: return requests.get(url) except Exception: time.sleep(1) return get(url) # 재시도 # 방법 2 while True: response = get(url) if response.status_code != 500: # 500, server error break else: time.sleep(1)
방법 1의 문제점은 url 문자열이 잘못되어있을 경우 무한정 요청 작업을 수행한다는 것이고, 방법 2의 문제점은 다양한 네트워크 에러를 로직이 올바르게 처리해주지 못한다는 점이다.
제안되는 솔루션은 다음과 같다.
import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry def requests_retry_session( retries=3, backoff_factor=0.3, status_forcelist=(500, 502, 504), session=None, ): session = session or requests.Session() retry = Retry( total=retries, read=retries, connect=retries, backoff_factor=backoff_factor, status_forcelist=status_forcelist, ) adapter = HTTPAdapter(max_retries=retry) session.mount('http://', adapter) session.mount('https://', adapter) return session
사용은 아래와 같이 한다. session 객체 전달은 선택 사양으로, 사용자가 session 객체를 따로 인자로 넘겨주지 않으면 requests_retry_session() 함수 내부에서 session 객체를 새로 생성하는 것을 위 코드에서 볼 수 있다. 그리고 status_forcelist엔 요청 실패로 간주할 응답 코드를 넣어줄 수 있다.
# 단순 get response = requests_retry_session().get(url) # 단순 get, timeout이 있는 경우. response = requests_retry_session().get(url, timeout=5) # 헤더 정보가 필요한 경우 s = requests.Session() s.auth = ('user', 'pass') s.headers.update({'x-test': 'true'}) response = requests_retry_session(session=s).get(url)
Retry 모듈과 HTTPAdapter 모듈을 이용해 요청을 할 때 재시도 정보를 포함시키는 것이다. backoff 팩터는 retries 값과 연계해 요청과 요청 사이에 쉬어줄 시간을 정해주게 된다.
쉬어줄 시간에 대한 로직은 다음과 같다.
슬립 = backoff factor * 2^(재시도 횟수 - 1)
따라서 위 함수의 기본 값으로 지정된 3과 0.3을 그대로 쓰는 경우
• 1회 재시도 sleep : 0초 (0.3 * 0)
• 2회 재시도 sleep : 0.6초 (0.3 * 2)
• 3회 재시도 sleep : 1.2초 (0.3 * 4)
값을 갖게 된다.
따라서 3회 요청에 실패하는 경우 사용자는 최대 1.8초 + @(통신 비용)을 기다리게 되는 셈이다.
위 함수는 당연히 post() 호출 시에도 유효하다.
출처: https://www.peterbe.com/plog/best-practice-with-retries-with-requests
아래는 유사 한글 포스팅 참고.
https://knight76.tistory.com/entry/python-%EC%9B%B9-%EC%9A%94%EC%B2%AD-%EC%98%88%EC%8B%9C-requests-HTTPAdapter-Retry
댓글 없음:
댓글 쓰기