Search
🌍

a2.6.1_1_1.1. title: ASGI는 ASGI 서버ASGI 애플리케이션으로 구성된다. 비동기함수 하나를 두고, ASGI 서버는 함수를 호출하는 타이밍을 정의하고, 함수 구현은 ASGI 애플리케이션이 한다.

생성
prev summary
🚀 prev note
next summary
🚀 next note
♻️ next note
a2.6.1_1_1.1.1. title: 전통적인 웹 아키텍처 관점으로는 ASGI 서버와 ASGI 애플리케이션을 통틀어 Nginx등의 웹 서버 뒤에 놓이는 웹 애플리케이션 서버(WAS)라고 본다.
a2.6.1_1_1.1.2. title: Django, FastAPI에서 동기 함수가 동시에 여러 개를 처리할 수 있는 것처럼 보일 때, 그 원인이 WSGI/ASGI 서버 레벨 처리일 수도 있고, WSGI/ASGI 애플리케이션 레벨 처리일수도 있다. ASGI에서 Django와 FastAPI의 Starlette는 Async outside, Sync inside 원칙으로 동기 컨트롤러를 실행하기 위해 별도의 스레드를 사용한다(스레드 풀에 던진다). WSGI/ASGI에 상관없이, Django 개발 서버(runserver)는 자체적으로 멀티 스레딩을 해 주기 때문에, 동시에 여러 개의 요청을 받을 수 있는 것처럼 보인다.
관련 임시노트
9 more properties
ASGI를 조금 더 잘 이해하기 위해 우선 “비동기 웹 서버와 웹 애플리케이션 간의 표준 인터페이스”라는 긴 문장 속에서 ‘비동기’라는 개념을 잠시 뒤로 미뤄두고 ‘웹 서버와 웹 애플리케이션 간의 표준 인터페이스’라는 부분을 먼저 이해해 보자. 어느정도 윤곽이 드러날 때 비동기 개념을 살짝 얹어 보기만 해도 ASGI 기반의 프레임워크를 사용하는 일에는 지장이 없을 정도로 충분하다.
ASGI의 정의에서도 드러나듯 ASGI는 두 개의 이해관계 구성원: ASGI 서버(ref1)ASGI 애플리케이션으로 구성된다(ref2). 게이트 인터페이스가 뭐고 어쩌고 하는 개념보다는 그냥 코드 조각 하나를 두고 이해하는 편이 훨씬 낫다. 아래 네 줄짜리 코드를 보자.
async def application(scope, receive, send): event = await receive() ... await send({"type": "websocket.send", ...})
Python
복사
FastAPI는 ASGI가 정의하는 이해관계 구성원 표현상 ASGI 애플리케이션이다. ASGI 애플리케이션이 되려면 위 함수를 구현하면 된다. 다시말해 “FastAPI는 ASGI 스펙을 준수하는 웹 프레임워크다.” 라는 말은 FastAPI가 위 함수를 구현했다는 말이다. FastAPI는 scope, receive, send라는 것을 전달받아 어떻게 처리를 해서 send({"type": "websocket.send", ...})를 리턴하는 방법을 정의했다. 이들 각각에 실제로 어떤 값이 들어 있는지, 지금은 별로 중요하지 않다. 모종의 약속된 값을 주고받았고, 위 코드 조각이 ASGI 서버와 ASGI 클라이언트가 어떤 방식으로 데이터를 주고받을지에 대한 규칙(인터페이스)이라는 점이 중요하다. 이렇게 약속대로 구현한 application 함수를 실행하는 주체가 바로 ASGI 서버다.
Uvicorn 같은 것들이 바로 ASGI 서버 구현체다. ASGI 서버는 클라이언트와 네트워크 연결을 관리하고, 요청을 받아 애플리케이션에 전달하는 역할을 한다. 웹 애플리케이션은 실제 요청을 처리해 응답을 생성해서 ASGI 서버 구현체로 넘기면 ASGI 서버가 클라이언트에게 최종 응답을 전송한다.
FastAPI는 async def application(scope, receive, send)을 어디 숨겨 두었을까? 바로 FastAPI의 부모 클래스 Starlette이다. 아래는 Starlette 소스코드인데, __call__ 을 보자.
class Starlette: """Creates an Starlette application.""" def __init__( self: AppType, debug: bool = False, routes: typing.Sequence[BaseRoute] | None = None, middleware: typing.Sequence[Middleware] | None = None, exception_handlers: typing.Mapping[typing.Any, ExceptionHandler] | None = None, on_startup: typing.Sequence[typing.Callable[[], typing.Any]] | None = None, on_shutdown: typing.Sequence[typing.Callable[[], typing.Any]] | None = None, lifespan: Lifespan[AppType] | None = None, ) -> None: ... async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: scope["app"] = self if self.middleware_stack is None: self.middleware_stack = self.build_middleware_stack() await self.middleware_stack(scope, receive, send)
Python
복사
FastAPI와 Uvicorn은 각각 ASGI 애플리케이션과 서버 역할을 한다고 했다. Uvicorn뿐 아니라 Daphne, Hypercorn 같은 다른 ASGI 서버도 결국 application(scope, receive, send)을 호출하고 반환값을 처리해 클라이언트에 응답한다. uvicorn main:app 이라는 명령어가 하는 일은, ASGI 웹 서버 Uvicorn에게 async def application(scope, receive, send)을 구현한 ASGI 애플리케이션이 main.py파일에 위치한 변수 app임을 알려주는 것이다. ASGI 웹 서버 Hypercorn에게 알려주는 명령어hypercorn main:app로 동일하다. 반대도 마찬가지다. 꼭 FastAPI가 아니더라도 ASGI 애플리케이션 인터페이스를 구현하는 프레임워크는 그 어떤 ASGI 서버에든 끼워 사용할 수 있다(ref3).
마지막으로 ASGI의 정의 “비동기 웹 서버와 웹 애플리케이션 간의 표준 인터페이스” 에서 “비동기”의 의미를 간단히 이해해 보자. 사실 과거에도 웹 서버와 웹 애플리케이션 간의 표준 인터페이스가 있었다. 그 이름은 WSGI였다.
def application(environ, start_response): ... return [response_body]
Python
복사
짠! WSGI와 ASGI의 가장 큰 차이는 인터페이스가 def application로 선언되었는가 아니면 async def application로 선언되었는가라고 정리할 수 있다(ref1). WSGI는 동기 함수를 인터페이스로 사용하기 때문에 비동기 요청을 받을 수 없었다. WSGI 서버 Gunicorn은 기본적으로 sync 워커를 생성한다. async def이 아니라, def application으로 구현된 함수를 실행하기 때문에 각 워커는 한 번에 하나의 요청만을 처리할 수 있다. 4개의 워커를 사용한다면 총 4개의 요청만을 동시에 처리할 수 있을 것이다. 반면 Uvicorn 워커는 application 소스코드가 await이 되는 순간 제어권을 곧바로 양보한다. 따라서 Uvicorn 워커(프로세스) 하나만으로도 I/O 바운드 병목을 무시하고 수많은 요청들을 동시에 처리할 수 있게 된다.
당시 파이썬으로 구현한 웹 애플리케이션은 제한된 목적으로만 사용될 수 있었을 것이다. 오늘날 ASGI를 지원하는 ASGI 서버와 애플리케이션은 비동기 함수로 작성되어 I/O 작업 중에 다른 요청을 처리할 수 있어 동시성(concurrency)이 크게 향상되었다.
parse me : 언젠가 이 글에 쓰이면 좋을 것 같은 재료을 보관해 두는 영역입니다.
1.
참고로 Django의 개발 서버를 실행하는 runserver 명령어는 내부적으로 멀티스레딩을 해 주지만, Gunicorn을 이용해 실행하는 순간 Gunicorn 워커의 방식으로 교체된다. 반면 FastAPI같은 경우에는 개발 서버라는 개념이 따로 없다. 실제로 프로덕션 환경에서도 Uvicorn 워커를 사용한다. 다만 그 앞단에 Gunicorn을 이용해서 Uvicorn 워커를 실행시킬 뿐이다.
from : 과거의 어떤 원자적 생각이 이 생각을 만들었는지 연결하고 설명합니다.
1.
앞의 메모에서는 비동기가 기본인 FastAPI 애플리케이션을 테스트할 때 발생할 수 있는 이슈에 대해 푸념하고 있다.
supplementary : 어떤 새로운 생각이 이 문서에 작성된 생각을 뒷받침하는지 연결합니다.
opposite : 어떤 새로운 생각이 이 문서에 작성된 생각과 대조되는지 연결합니다.
1.
None
to : 이 문서에 작성된 생각이 어떤 생각으로 발전되거나 이어지는지를 작성하는 영역입니다.
ref : 생각에 참고한 자료입니다.