Python 비동기 처리에서 발생한 Event Loop 블로킹 해결
문제 상황
FastAPI로 구축한 이미지 처리 API에서 간헐적으로 응답 지연이 발생했다. 모니터링 결과 특정 엔드포인트 호출 시 다른 요청들까지 수 초간 대기하는 현상이었다.
@app.post("/process-image")
async def process_image(file: UploadFile):
image_data = await file.read()
result = heavy_image_processing(image_data) # 문제 지점
return {"result": result}
원인 분석
heavy_image_processing 함수가 CPU 집약적 동기 함수였다. async 함수 내에서 동기 함수를 직접 호출하면 event loop가 블로킹되어 다른 코루틴들이 실행되지 못한다.
해결 방법
run_in_executor를 사용해 별도 스레드 풀에서 처리하도록 변경했다.
import asyncio
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=4)
@app.post("/process-image")
async def process_image(file: UploadFile):
image_data = await file.read()
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(
executor,
heavy_image_processing,
image_data
)
return {"result": result}
CPU 집약적 작업의 경우 ProcessPoolExecutor도 고려할 수 있지만, 이미지 데이터 직렬화 오버헤드를 고려해 ThreadPoolExecutor를 선택했다.
결과
동시 요청 처리 시 응답 시간이 안정화되었다. Python의 비동기 처리는 I/O bound 작업에 효과적이지만, CPU bound 작업은 별도 처리가 필요하다는 점을 다시 확인했다.