Python 비동기 처리 시 동기 함수 호출 문제 해결
문제 상황
FastAPI로 구축한 API 서버에서 특정 엔드포인트의 응답 시간이 비정상적으로 길어지는 현상이 발생했다. 프로파일링 결과, 비동기 엔드포인트 내에서 서드파티 라이브러리의 동기 I/O 함수를 직접 호출하면서 이벤트 루프를 블로킹하고 있었다.
@app.get("/report")
async def generate_report(user_id: int):
# 문제: 동기 함수가 이벤트 루프 블로킹
result = legacy_report_generator.create(user_id)
return result
해결 방법
Python 3.9부터 제공되는 asyncio.to_thread()를 사용해 동기 함수를 별도 스레드에서 실행하도록 수정했다. 이전 버전의 경우 loop.run_in_executor()를 사용할 수 있다.
import asyncio
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=10)
@app.get("/report")
async def generate_report(user_id: int):
# Python 3.9+
result = await asyncio.to_thread(
legacy_report_generator.create,
user_id
)
return result
# Python 3.8 이하
@app.get("/report-legacy")
async def generate_report_legacy(user_id: int):
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(
executor,
legacy_report_generator.create,
user_id
)
return result
결과
평균 응답 시간이 2.3초에서 0.8초로 개선되었고, 동시 요청 처리 시에도 다른 엔드포인트가 블로킹되지 않게 되었다. 동기 라이브러리를 완전히 교체하기 어려운 상황에서 실용적인 해결책이었다.
주의사항
- ThreadPoolExecutor의 worker 수는 동기 I/O 작업 특성에 맞게 조정 필요
- CPU 바운드 작업은 ProcessPoolExecutor 고려
- 가능하면 네이티브 async 라이브러리 사용이 최선