궁금한게 많은 개발자 노트

[ FastAPI ] 비동기 메커니즘 본문

Back End

[ FastAPI ] 비동기 메커니즘

궁금한게 많은 개발자 2023. 12. 11. 10:10

FastAPI 프레임워크의 구성요소와 FastAPI가 사용하는 비동기 메커니즘에 대해 알아보고자 합니다.

우선 FastAPI가 각광받는 이유로는 높은 속도와 성능(Starlette와 Pydantic), ASGI를 기반한 비동기(async/await), 코드 변화에 따른 API문서화(openAPI)등을 꼽을 수 있습니다.

 

WSGI(Web Server Gateway Interface): 웹서버와 웹프레임워크 사이에서 통신하기 위한 인터페이스 (동기) ex) gunicorn
ASGI(Asynchronous Server Gateway Interface): WSGI를 계승하고, Asyncio라이브러리를 이용한 비동기 처리 가능

 

높은 속도와 성능이 보장되는 이유는 FastAPI는 ASGI서버인 Starlette 프레임워크를 기반으로 동작하기 때문입니다.

Starlette은 Pyhon3.6이상 고성능 비동기 서비스를 구축하는 데 이상적이고 가벼운 ASGI 프레임워크/툴킷으로, Starlette을 직접 사용하는 것에 비하면 성능은 낮을 수 있지만, 개발 속도 개선 및 다양한 이점을 가질 수 있습니다.

또, 이처럼 Starlette이 강력한 성능을 가지는 이유는 내부적으로 Uvicorn을 사용하고 있기 때문입니다. Uvicorn은 uvloop( (libuv기반 Cython으로 작성되어 빠른 속도 가능)와 httptools를 이용하여 구현한 Python 전용 프로세스 기반 초고속 ASGI 서버입니다. 즉, FastAPI가 빠른 성능을 가지는 것은 Uvicorn을 내부적으로 사용하고 있기 때문입니다.

 

pip3 install fastapi
pip3 install uvicorn[standard]

위와 같이 uvicorn을 standard로 설치 시 Cython기반의 디펜던시가 설치됩니다. 또한 이벤트 루프로 uvloop이 사용되고 http프로토콜은 httptools로 처리됩니다. 설치 시 stadard로 설치하지 않으면, uvloop가 설치되지 않고 이벤트 루프로 asyncio가 사용되어 성능이 낮아질 수 있습니다.

 

 

FastAPI는 ASGI인 Uvicorn덕분에 비동기 처리가 가능한데, Python은 Race Condition을 방지하기 위해 GIL(Global Interpreter Lock)을 이용하기에 Multi Thread환경을 정상적으로 지원하지 못합니다. ( Python의 GIL은 사실 CPU Bound작업에 대해서만 적용이 되고, I/O Bound 작업에 대해서는 내부적으로 해제가 된다고 합니다.) 즉, 특정 코드에 대해 한번에 하나의 스레드만 사용할 수 있다는 의미입니다. 하지만 비동기적인 특성을 위해서는 다수의 쓰레드를 활용해야 하는데, 어떻게 Uvicorn은 이것을 가능하게 했을까요?

https://docs.python.org/3.10/library/multiprocessing.html#contexts-and-start-methods

 

multiprocessing — Process-based parallelism — Python 3.10.13 documentation

Source code: Lib/multiprocessing/ Introduction multiprocessing is a package that supports spawning processes using an API similar to the threading module. The multiprocessing package offers both local and remote concurrency, effectively side-stepping the G

docs.python.org

결과적으로 Uvicorn은 단일 프로세스로 비동기를 지원합니다. 단일 프로세스의 한계를 넘기위해, 처리량을 늘리기 위해서는 Gunicorn과 함께 사용하는 멀티 프로세싱 방식을 이용한다고 합니다. 새로운 프로세스를 생성하는 방식으로는 fork, spawn등이 있습니다. 이를 사용하여 효과적으로 GIL을 회피하여 효과적으로 비동기식 처리가 가능합니다.

 

fork: 부모 프로세스의 상태와 메모리를 복제하여 자식 프로세스를 생성, 프로세스간 메모리를 공유 (좀 더 빠름)
spawn:부모 프로세스의 메모리와 상태를 복제하지 않고 새로운 프로세스를 생성, 프로세스간 메모리 공유X

 

WSGI인 Gunicorn을 사용하면 Gunicorn이 웹서버이자 프로세스 관리자 역할을 수행해주어, 여러개의 Uvicorn을 worker 로 사용한 멀티 프로세싱 환경을 구축할 수 있습니다.

gunicorn main:app --workers 3 --worker-class uvicorn.workers.UvicornWorker --daemon --access-logfile ./log.log

 

FastAPI 공식문서에서도 비동기 서버를 구동해야 할 경우에도 worker를 지정하는 경우에는 gunicorn을 사용하는게 좋다고 안내하고 있습니다. worker들을 관리하는 프로세스 관리자 역할을 수행하기에는 uvicorn이 부족하다고 합니다.

즉, gunicorn은 특정 worker 프로세스 클래스를 사용해서 프로세스 매니저의 기능을 지원하며, uvicorn은 gunicorn에 호환가능한 worker 클래스를 가지고 있습니다. 이 조합을 이용하여 uvicorn의 호환 클래스는 gunicorn에서 보낸 데이터를 FastAPI에서 사용할 수 있도록 ASGI 표준으로 변환해준다고 합니다.

 

사실, 클라우드 환경에서 쿠버네티스를 사용한다면 worker를 하나만 사용하면서 uvicorn을 사용해도 무리가 없어 보입니다. 프로세스 매니저 역할을 쿠버네티스에서 컨테이너 오케스트레이션 기능을 통해 대체할 수 있기 때문입니다. 사용하는 환경에 따라 적절히 조합하여 웹서버를 구축하는 것이 좋을 것 같습니다.

 

https://livvjh.com/posts/develop/fastapi-beginner/

 

FastAPI를 시작할때 보면 도움되는 정보 정리

1. 정리의 목적 FastAPI? 요새 다 좋다고 소문났던데? 빠르고 가벼운 프레임워크? 비동기? 라는 추상적인 내용에 가볍게 프로젝트를 시작해보는 경우가 많습니다. 공식 문서를 참고하며 프로젝트를

livvjh.com

 

Comments