앞선 글 2023.03.21 - [CS/Python] - FastAPI의 동기 함수 동작 방식에서 FastAPI는 서버 Blocking을 막기 위해 동기 함수를 스레드풀로 넘겨 작업한다는 것을 확인했습니다. 그럼 비동기 함수는 어떻게 동작할까요? FastAPI를 만든 tiangolo가 한 이슈에 단 답변으로 이를 확인할 수 있습니다.
Any async function can only be run on the main thread, on the main event loop. There's no other way around in Python. So, if there's an async path operation (route) function or an async dependency function, know that it can only be run on the main thread, on the main event loop, and that's what FastAPI will do.
동기 함수와 달리 비동기 함수는 FastAPI의 Main thread에서 동작한다고 합니다. 이를 코드로 확인해보겠습니다.
import asyncio
import threading
print(f"Main thread id: {threading.get_ident()}") # Print main thread id
@app.get("/async_with_await")
async def async_with_await():
tid = threading.get_ident()
print(f"Hello {tid}")
await asyncio.sleep(2)
print(f"Bye {tid}")
# In run_sync.txt
url = "http://127.0.0.1:8000/async_with_await"
url = "http://127.0.0.1:8000/async_with_await"
# Run parallel requests using curl
curl --parallel --parallel-immediate --parallel-max 2 --config run_async_with_await.txt
Main thread id: 8666859008
INFO: Started server process [15211]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Hello 8666859008
Hello 8666859008
Bye 8666859008
INFO: 127.0.0.1:51683 - "GET /async_with_await HTTP/1.1" 200 OK
Bye 8666859008
INFO: 127.0.0.1:51684 - "GET /async_with_await HTTP/1.1" 200 OK
결과를 보면 설명과 같이 메인 스레드(8666859008)에서 모든 요청이 처리된 것을 확인할 수 있습니다.
위 결과를 통해 생각해보면 동기 함수로 처리할 수 있는 것을 비동기 함수로 실행했을 때 문제점을 확인할 수 있습니다. 코드로 확인해보겠습니다.
@app.get("/async_without_await")
async def async_without_await():
tid = threading.get_ident()
print(f"Hello {tid}")
time.sleep(2)
print(f"Bye {tid}")
Main thread id: 8666859008
INFO: Started server process [15211]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Hello 8666859008
Bye 8666859008
INFO: 127.0.0.1:51707 - "GET /async_without_await HTTP/1.1" 200 OK
Hello 8666859008
Bye 8666859008
INFO: 127.0.0.1:51708 - "GET /async_without_await HTTP/1.1" 200 OK
결과를 보면 비동기 함수를 실행했음에도 불구하고 일반적인 파이썬 동기 함수가 동작하듯이 순서대로 요청이 처리된 것을 확인할 수 있습니다. 이는 FastAPI의 비동기 함수는 하나의 메인 스레드에서 동작하기 때문에 CPU-Bound가 걸려 전체 서버가 Blocking되기 때문에 발생한 결과입니다. 이런 방식으로 비동기 함수를 이용할 경우 멀티 스레드로 처리되는 동기 함수에 비해 전체 요청이 처리되는데 소요되는 시간이 더 길어집니다.
일반적으로는 다수의 Worker를 이용하거나, K8s와 같은 서비스를 통해 다수의 프로세스를 이용하기 때문에 서버가 Blocking되는 이슈가 생기는 일은 적겠지만, 근본적으로 이런 문제를 방지하기 위해서는 FastAPI에서 비동기 함수를 이용할 때 IO-Bound가 큰 작업인지 다시 한 번 확인을 해야합니다.
잘못된 내용, 오타, 부정확한 문장 등 어떤 피드백이든 환영합니다. 감사합니다.
References
'CS > Python' 카테고리의 다른 글
[FastAPI]FastAPI의 동기 함수 동작 방식 (0) | 2023.03.21 |
---|---|
[Python] Class에서 특정 문자열로 시작하는 Method 찾기 (0) | 2022.05.25 |
[Python] 주어진 문자열이 알파벳/숫자로만 구성되었는지 확인해보기 (0) | 2022.04.20 |