FastAPI는 requests와 같은 non async 라이브러리도 async하게 실행시켜줍니다.
공식 문서에 따르면 non async 함수는 별도의 스레드풀을 이용하여 async하게 처리한다고 합니다.
FastAPI는 실행하려는 스코프가 def로 선언되어있는 경우 별도의 스레드풀을 통해 non-async 를 async하게 처리합니다.
# Non-block
@router.get("/sync/sync")def sync():
time.sleep(10)
logger.info('running...')
하지만 스코프가 async로 선언되어있는 경우는 non-async 함수를 이벤트루프에서 처리하여 서버가 block됩니다.( --worker
가 1(default)인 경우)
# Block!!
@router.get("/async/sync") async def async_with_sync():
time.sleep(10)
logger.info('running...')
이벤트 루프를 block 시키지 않게하려면 아래와 같이 호출하려는 non-async 함수를 run_in_threadpool
을 사용해 명시적으로 처리해주면 됩니다.
# Non-Block
@router.get("/async/sync")async def async_with_sync():
from fastapi.concurrency import run_in_threadpool
await run_in_threadpool(sleep, 10)
logger.info('running...')
또는 --worker
옵션을 통해서 non-async 를 async하게 처리할 수 있습니다.
단 이 경우 생성하는 워커 수에 대해서만 비동기가 보장됩니다.
만약
uvicorn src.app:app --port 8000 --workers 3
위와 같이 3개의 worker 실행시킨다고 하면,
# Block when 4th request incoming
@router.get("/async/sync")async def async_with_sync():
time.sleep(10)
logger.info('running...')
위 API는 동시에 3 request (worker 수)만 처리할 수 있습니다.
만약 4번째 request가 time.sleep중에 발생하면 10초가 지난 뒤에 처리가 가능합니다.