FastAPI에서의 동시성 처리
스프링 같은 경우는 Tomcat (서블릿 컨테이너) 을 웹 서버로 사용해 동시성을 처리한다.
서버 컴퓨터에다가 자바 기반으로 작성된 Tomcat을 프로세스로 실행시키고 Tomcat 프로세스의 쓰레드로 스프링 애플리케이션을 실행시킨다.
Tomcat에 동시에 100명의 사용자가 접속하는 경우 Tomcat은 100개의 쓰레드를 생성하고 (쓰레드 풀의 개수에 따라 달라질 수 있다) 각 쓰레드들은 디스패처 서블릿으로 접근하고 디스패처 서블릿은 요청을 처리하는 메서드로 쓰레드들을 안내해준다.
이런 방식으로 멀티쓰레딩 방식으로 동시성을 처리하고, 각 쓰레드들은 스프링 컨테이너의 빈을 공유하며 메모리를 효과적으로 사용한다.
파이썬은 자바와는 다르게 인터프리터 언어이고, Global Interpreter Lock 이라는 개념을 사용한다.
GIL은 한 번에 하나의 쓰레드만 파이썬 바이트코드를 실행할 수 있도록 하는 파이썬의 규약이고, GIL 때문에 파이썬에서는 자바에서처럼 멀티쓰레딩을 활용할 수 없다.
스프링에서는 Tomcat을 웹 서버로 사용하듯, 파이썬에서는 비슷한 역할을 하는 요소로 ASGI와 WSGI가 있다.
1. WSGI
Web Server Gateway Interface의 약자로, 파이썬에서 웹 서버와 웹 애플리케이션간의 통신 규약을 의미한다.
WSGI는 이름처럼 동기적 환경에서 작동하고, WSGI의 구현체로는 Gunicorn과 uWSGI 등이 있다.
Gunicorn은 마스터 프로세스가 여러 워커 프로세스를 생성하고 관리하는 프리 포크 모델을 사용하는 웹 서버로, 각 워커 프로세스는 독립적으로 클라이언트의 요청을 처리할 수 있다.
이렇듯 Guncorn은 파이썬의 GIL을 우회하기 위해 여러 "프로세스" 를 만들어서 요청을 처리하는 멀티프로세싱 기반 웹 서버이고, 스프링의 쓰레드들은 Tomcat의 쓰레드 풀에서 관리되듯 워커들은 프로세스 풀에서 관리된다.
(옵션을 통해 멀티쓰레드 방식도 사용할 수 있다)
프로세스 풀의 개수가 20이고 50개의 동시 요청이 발생했을 경우 20개의 프로세스만 만들고 각 프로세스들은 별도의 워커 (파이썬 인터프리터) 에서 처리된다.
uWSGI는 멀티쓰레딩과 멀티프로세싱을 모두 지원하는 웹 서버, 설정에서 쓰레드와 프로세스 개수를 조정할 수 있다.
여기서 멀티쓰레딩은 스프링이 처리하는 방식과 다름에 주의하자.
스프링에서의 멀티쓰레딩은 하나의 프로세스 내에서 여러 쓰레드를 생성한 후 작업을 병렬로 수행하는 기법이다.
각 쓰레드는 프로세스의 데이터와 힙 공간을 공유하고, 자원을 효과적으로 사용할 수 있고 Context Switching 비용이 적다는 장점이 있는데...
파이썬에서는 GIL 규약 때문에 사실상 동시에 여러 쓰레드를 실행시키는 것이 불가능하다.
I/O 바운드 작업에서는 쓰레드가 대기 상태가 돼 GIL이 해제되니 다른 쓰레드가 작업을 수행할 수 있고, 한 쓰레드가 I/O 바운드 작업에 걸렸다면 다른 쓰레드를 실행해 바운드 작업을 대기하지 않고 이어서 작업을 수행할 수 있어 마치 여러 쓰레드가 동시에 작동하는 것 "처럼" 보이지만, 사실은 그렇지 않다.
2. ASGI
Asynchronous Server Gateway Interface의 약자로 역시 통신 규약을 의미하지만 비동기로 작동한다.
ASGI의 구현체로는 uvicorn이 있다. 클라이언트의 요청을 받는 서버는 uvicorn이고, uvicorn이 파이썬으로 작성한 코드를 실행시켜 요청을 처리한다.
uvicorn은 내부적으로 비동기 이벤트 루프를 사용해 요청을 처리하는데, 동작 방식이 멀티쓰레딩과는 다르다.
동시에 100명이 요청을 보내면 uvicorn은 각 요청을 코루틴이라고 불리는 비동기 태스크로 처리한다.
각 요청은 개별 코루틴으로 실행되고, 이 코루틴들은 이벤트 루프에 의해 관리된다.
요청 처리 중 I/O 작업이 발생하면 해당 작업이 비동기적으로 처리되고, 이벤트 루프는 다른 코루틴으로 전환해서 끊기지 않고 요청을 처리한다.
즉, 하나의 프로세스에서 하나의 쓰레드만 사용하고 하나의 쓰레드에서 여러 개의 코루틴을 동시에 수행하는 것 "처럼" 작업한다. (파이썬의 멀티쓰레딩과 비슷하다)
이벤트 루프는 비동기 프로그래밍에서 사용되는 개념으로, I/O 작업 관련 비동기 태스크를 관리할 때 사용된다.
코루틴은 프로그램 실행 중 시작 중단 재개가 가능한 루틴으로, 함수와는 다르게 실행이 중단된 후 추후 그 지점부터 다시 실행할 수 있으며 상태를 유지한다.
이렇게만 들었을 때 멀티쓰레딩보다 코루틴을 활용한 처리가 훨씬 더 좋은 것 같지만.. 항상 그렇듯 상황에 적합한 기술을 사용하자.
ASGI와 WSGI는 서버 인터페이스고 그 구현체로는 Gunicorn, uvicorn 등이 있다.
Starlette은 ASGI 사양을 구현하는 파이썬 웹 프레임워크로, 라우팅 요청 및 응답 처리, 세션 관리 기능을 포함한다.
FastAPI는 Starlette에 기반을 두고 Pydantic을 사용하는 파이썬 웹 프레임워크로, Starlette의 모든 기능을 포함하며 추가적으로 문서화 등 다양한 편의 기능을 제공한다.
비동기 프로그래밍은 특정 코드의 실행이 완료될 때 까지 기다리지 않고 다른 작업을 동시에 진행하는 프로그래밍 방식을 의미한다.
FastAPI에서 메서드를 작성할 때 async와 await 문법을 사용하고, 이 async def 로 작성된 함수는 코루틴을 정의한다.
함수 호출 즉시 실행하는 대신 실행할 준비가 된 코루틴 객체를 반환하고, await는 코루틴의 실행을 일시 중단하고 비동기로 실행할 수 있는 다른 작업으로 제어를 넘긴다.
async 만 사용하고 await를 사용하지 않으면 그냥 동기 프로그래밍과 다를게 없으니... await를 적절하게 사용해 주자.
파이썬에서의 멀티쓰레딩은 OS가 직접 컨텍스트 스위칭을 관리해 주지만, 코루틴을 사용하는 비동기 프로그래밍을 수행하는 경우 개발자가 직접 await를 통해 어디서 코루틴을 전환할 지 명시해 줘야 한다.
'FastAPI > FastAPI' 카테고리의 다른 글
[FastAPI] SQLAlchemy (0) | 2023.11.06 |
---|---|
[FastAPI] 요청과 응답 (0) | 2023.11.02 |
댓글
이 글 공유하기
다른 글
-
[FastAPI] SQLAlchemy
[FastAPI] SQLAlchemy
2023.11.06 -
[FastAPI] 요청과 응답
[FastAPI] 요청과 응답
2023.11.02