FastAPI 에서 non-async 함수 비동기로 처리하기

Key Kim
3 min readAug 16, 2021

--

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초가 지난 뒤에 처리가 가능합니다.

--

--