[Python] requests Transport Adapters - 요청 재시도
개요
이전에 Python으로 HTTP 요청을 구현하면서 2024.07.18-[Python] Requests hooks - 응답에 대한 콜백 구현하기에서 살펴봤던 hooks를 활용해 재시도 로직을 구현했었다. 그런데 최근에 requests 모듈에서 제공하는 Transport Adapters를 활용해서 재시도 로직을 구현할 수 있다는 것을 알게 되었다.
방법을 정리해 보자.
Transport Adapters
전송 어댑터(Transport Adapters)는 HTTP 서비스의 상호 작용 방식을 제어하는 메커니즘을 제공하며, 서비스별 구성을 적용할 수 있다.
Requests는 단일 전송 어댑터인 HTTPAdapter와 함께 제공된다. HTTPAdapter는 urllib3을 사용해 HTTP와 HTTPS 간의 기본적인 상호작용을 제공한다. Requests Session이 초기화될 때마다, 각각은 HTTP Session 객체에, HTTPS Session 객체에 연결된다.
사용자는 필요에 맞게 전송 어댑터를 생성하고 사용할 수 있다. 생성한 전송 어댑터는 적용할 웹 서비스 정보화 함께 Session 객체에 마운트 한다. mount을 호출하면 특정 전송 어댑터를 prefix에 등록한다. 마운트가 되면 해당 Session을 사용해 URL이 지정된 prefix로 시작하는 HTTP 요청은 모두 전송 어댑터를 사용하게 된다.
자동 재시도 구현
기본적으로 Requests는 실패가 발생한 연결을 재시도하지 않는다. 하지만 the urllib3.util.Retry 클래스를 사용해 Session 내에 backoff를 포함하는 자동 재시도 기능을 구현할 수 있다.
Retry 객체 정의
먼저 재시도 로직을 구현하는 Retry 객체를 정의한다. 아래 코드는 최대 3번의 요청을 시도하고, HTTP 오류 상태 코드가 403, 429, 500, 502, 503, 504인 경우에만 재시도를 수행하도록 지정한 예시다.
from urllib3.util import Retry
# define the retry strategy
retry_strategy = Retry(
total=3, # maximum number of retries
status_forcelist=[
403,
429,
500,
502,
503,
504,
], # the HTTP status codes to retry on
)
HTTPAdapter 정의
재시도 로직을 포함하는 HTTPAdapter를 정의한다.
from requests.adapters import HTTPAdapter
# create an HTTP adapter with the retry strategy and mount it to the session
adapter = HTTPAdapter(max_retries=retry_strategy)
Session 객체에 HTTPAdapter 마운트
아래 코드는 HTTP, HTTPS 요청에 위에서 정의한 HTTPAdapter를 사용하도록 마운트 한 예시이다.
import requests
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)
전체 코드는 접은글로 적어둔다.
import requests
from requests.adapters import HTTPAdapter
from urllib3.util import Retry
# define the retry strategy
retry_strategy = Retry(
total=3, # maximum number of retries
status_forcelist=[
403,
429,
500,
502,
503,
504,
], # the HTTP status codes to retry on
)
# create an HTTP adapter with the retry strategy and mount it to the session
adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)
요청 테스트
간단하게 s3에 저장되어 있는 파일에 접근을 시도해 보자. 별도의 인증을 진행하지 않았기 때문에 403 상태 코드를 받고 재요청을 시도할 것이다.
Session 객체를 사용하는 요청도 기존 request 객체와 동일하게 요청하며 된다.
url = 'https://test.com'
print(session.get(url))
테스트해 보면 403 오류로 인해 재시도 횟수를 초과하여 에러가 발생한 것을 확인할 수 있다.
다만 출력이나 로그 등으로 재시도 여부를 확인할 수 없다는 점이 조금 불편한 것 같다.
참고 문서
https://requests.readthedocs.io/en/latest/user/advanced/
https://devocean.sk.com/blog/techBoardDetail.do?ID=164130
https://www.zenrows.com/blog/python-requests-retry