[Python] 파이썬 웹서버 & Flask vs. FastAPI

2022. 10. 26. 00:31TIL💡/Python

평소에 Flask보다 FastAPI가 더욱 성능이 좋다는 것은 알고 있는데, 왜 그럴까?

 

왜냐하면 Flask가 WSGI 기반임에 불구하고, FastAPI는 ASGI 기반으로 개발되었기 때문이다.

 

우선 둘의 차이점을 알아보기 이전에, Spring 계열에서는 웹서비스를 Tomcat 등의 WAS로 배포를 하는데, 공교롭게도 Python에서는 이러한 WAS를 제공하지 않는다.

그렇다면 어떻게 웹서버와 Python  애플리케이션을 연결하는 것일까?

CGI(Common Gateway Interface)와 FastCGI

 

CGI를 이용하면 웹서버에 들어온 요청으로 파이썬 프로그램을 실행하고, 그 결과를 각각의 브라우저로 보여줄 수 있게 된다.

과거에는 정적 HTML 파일 하나만 가지고 웹 서비스를 하였기 때문에 CGI가 필요하지 않았다.

웹 서버가 정적 파일 등을 사용자에게 다운로드 방식으로 제공하는 것이 전부인데, 웹에 대한 수요가 증가함에 따라 웹 서버가 처리할 수 없는 정보가 요청되었을 경우 그 처리를 외부 애플리케이션이 할 수 있도록 호출함으로써 중계 역할을 해준다.

 

그러나 CGI는 클라이언트의 요청이 발생할 때마다 프로세스를 추가로 생성하고 삭제하게 된다.

예를 들어 위 그림과 같이 다수의 사용자가 동시에 요청할 경우 새로운 프로세스를 생성하고, 삭제하는 것이 빈번해지는데 이는 커널 리소스를 계속 생성/삭제하기 때문에 오버헤드가 심해지고, 성능 저하의 원인이 되기도 한다.

 

현대의 웹서비스처럼 빈번하게 REST API가 호출되고, 요청과 응답을 수시로 반복하여 처리하는 곳에서 이러한 아키텍처는 맞지 않다. 그래서 생긴 것이 FastCGI이다.

 

FastCGI

FastCGI는 몇 번의 요청이 들어와도 하나의 프로세스만을 가지고 처리하게 된다.

즉 메모리에 단 하나의 프로그램만을 적재하여 계속 재활용하기 때문에 CGI에 비하여 오버헤드가 급격하게 감소한다.

 

Java의 Tomcat 또한, Web Server + FastCGI를 채택한 형태로 다수의 사용자 접속에 유리하다.

우리는 JVM 계열의 언어로 서버 프로그램을 작성하면 JBoss, Tomcat 등의 웹 애플리케이션 서버를 사용하는 게 보편적이지만 이외에도 우리가 CGI를 수동으로 붙여서 사용할 수 있는 방법도 있다.

 

그러나 파이썬에는 이러한 WAS가 별도로 존재하지 않으며 결국 우리가 CGI, FastCGI 등을 이용해서 원하는 WAS 형태를 만들어 사용해야 하는데, 실제로 파이썬에서는 파이썬만의 게이트웨이 인터페이스가 존재한다.

 

만약 CGI에 요청이 너무 많아지면 프로세스에 부하가 걸려서, 요즘에는 WSGI나 ASGI를 사용한다.

 

💙 WSGI: Web Server Gateway Interface

💙 ASGI: Asynchronous Server Gateway Interface

 

둘의 차이는 무엇일까?_?

 

WSGI(Web Server Gateway Interface)

Python 애플리케이션, 스크립트가 웹 서버와 통신하기 위한 인터페이스로써 CGI 디자인 패턴을 모태로 하여 만들어진 것이지만 실제 CGI와는 다소 차이가 있다.

WCGI는 CGI와 동일하게 웹 서버와 애플리케이션 중간에 위치하는데,CGI와 다른 점은 CGI는 매 요청마다 프로세스를 생성한다는 점이고, WSGI는 한 프로세스에서 모든 요청을 받는다는 것이다. 좀 더 디테일하게 설명을 하자면, CGI는 매 요청마다 Fork 등의 함수를 통해 커너 리소스를 추가/반납하고, WSGI는 많은 요청을 콜백(callback)으로 받아 처리하게 된다.

 

이러한 WSGI로 대표적인 두 가지 방법이 있다.

1. nginx, apache에서 내장 모듈로 제공하는 server-often high profile 방식(ex.mod-wsgi, mod-python 등 파이썬 기반)

2. Python 코드로 작성된 Web App Server(ex. gunicorn, cherrypy 등)

 

결국 WSGI는 웹 서버와 애플리케이션 사이에 미들웨어 역할을 하며 기술적으로는 웹 서버도 WSGI에 대한 작동 코드가 필요하고, 애플리케이션 또한 WSGI에 대한 작동 코드가 필요한 Client-Server Model을 응용한 것이라고 봐도 무방하다.

 

 

ASGI(Asynchronous Server Gateway Interface)

WSGI만으로도 우리는 충분히 클라이언트의 요청을 웹 서버가 받아 내 애플리케이션으로 올 수 있도록 구성할 수 있었다.

하지만 공교롭게도 이것만으로 현대 웹 서비스의 대용량 트래픽 처리를 유연하게 하기 위한 만족된 조건을 충족하기 어려운 점이 있다.

 

Python에서는 asyncio, coroutine 과 같은 비동기 처리를 지원한다. 그러나 WSGI는 동기 함수 처리만을 지원하여 여러 작업을 동시에 처리하는 것에 한계가 있다. 가령 현대 웹 서비스에서는 웹 소켓 등을 사용한 실시간 채팅 서비스를 할 수 있는, WSGI로는 이러한 서비스를 구현하는 데 어려움이 있다.

 

물론 안되는 것은 아니다. 비동기 큐와 같은 Celery를 잘만 사용한다면 대용량 트래픽 처리를 요구하는 서비스 구현이 불가능한 것은 아니지만, 트레이싱 등과 같은 유지 보수, 기본적인 구현이 쉽지 않다는 점이 있다.

 

따라서 최근에는 Django 3.0 뿐만 아니라 FastAPI 등의 프레임워크에서도 ASGI 인터페이스를 적용하였으며 뒤따라 Falcon 프레임워크도 3.0부터 ASGI 개발에 들어갔다.

운영 아키텍처로 봤을 때는 크게 다르지 않다. 그러나 WSGI와 다르게 ASGI는 기보적으로 요청을 비동기적으로 처리한다는 점이 크다.

따라서 WSGI에서는 지원되지 않는 Websocket 프로토콜과 HTTP 2.0을 지원한다.

 

이러한 대표적인 ASGI Web App 에는 uvicorn이 있다. uvicorn은 ASGI 기반의 웹 애플리케이션 서버로서 그 내장 모듈로 uvloop을 사용한다.

uvloop에서 uv는 libuv 즉, Javascript V8에서 사용되는 비동기 모듈을 사용한 것이다.

Github에서 uvloop는 Cython에 구현되어 있다고 한다. uvloop는 asyncio를 2~4배 빠르게 해준다.

ASGI는 Cython 기반으로 C++언어로 작성되어 매우 빠른 속도를 제공한다는 것이 특징인데다가 libuv를 사용하여 비동기 처리를 하니 Node.js와 같은 비동기 처리 속도를 어느 정도 누릴 수 있다는 장점이 있다.

 

Uvicorn is an ASGI web server implementation for Python.

Untial recently Python has lacked a minimal low-level server/application interface for async frameworks.
The ASGI specification fills this gap, and means we're now able to start building a common set of tooling usable across all async frameworks.

Uvicorn currently supports HTTP/1.1 and WebSockets.

여기에 WSGI와 ASGI의 차이점이 명확하게 언급된다. ASGI는 비동기 처리를 위해 connection scope를 도입하여 커넥션이 닫힐 때까지 살아있도록 유지하게 하고, event를 도입해 이벤트는 메세지로서 애플리케이션에 전송되고, 메세지는 다시 애플리케이션에 의해 서버로 보내진다. 이러한 모든 작동은 asynchronous event loop 안에서 발생한다.

 

 

추가적으로 공부할 부분

- 코루틴

- GIL

- HTTP 1.1, 2.0

 

 

 

 

참고

https://blog.neonkid.xyz/249

 

 

'TIL💡 > Python' 카테고리의 다른 글

[Python] @classmethod와 @staticmethod의 차이  (0) 2022.10.20