Python 비동기 처리에서 asyncio.gather 예외 처리
문제 상황
외부 API 5개를 동시에 호출해 데이터를 집계하는 기능을 구현했다. asyncio.gather로 병렬 처리했는데, 한 개 API만 실패해도 전체가 실패하는 문제가 있었다.
async def fetch_all():
results = await asyncio.gather(
fetch_api_a(),
fetch_api_b(),
fetch_api_c()
)
return results
API 하나가 타임아웃되면 전체 결과를 받지 못했다.
해결 방법
return_exceptions=True 옵션을 사용하면 예외가 발생해도 계속 진행되고, 결과 리스트에 예외 객체가 포함된다.
async def fetch_all():
results = await asyncio.gather(
fetch_api_a(),
fetch_api_b(),
fetch_api_c(),
return_exceptions=True
)
# 성공한 결과만 필터링
valid_results = [
r for r in results
if not isinstance(r, Exception)
]
# 실패 로깅
for i, r in enumerate(results):
if isinstance(r, Exception):
logger.error(f"API {i} failed: {r}")
return valid_results
추가 개선
각 API마다 재시도 로직을 추가했다. tenacity 라이브러리를 사용하면 간단하게 구현할 수 있다.
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10))
async def fetch_api_a():
async with aiohttp.ClientSession() as session:
async with session.get(url, timeout=5) as resp:
return await resp.json()
이렇게 수정한 후 부분 장애 상황에서도 서비스가 정상 동작하게 되었다. 모니터링 결과 전체 실패율이 5%에서 0.3%로 감소했다.