FastAPI + Uvicorn: 엄청난 속도의 기술, 그 뒷이야기를 알아볼까요?

FastAPI + Uvicorn: 엄청난 속도의 기술, 그 뒷이야기를 알아볼까요?

Uvicorn이 무엇일까요?

답변: Uvicorn은 uvloop와 httptools를 기반으로 구축된 매우 빠른 ASGI(Asynchronous Server Gateway Interface) 서버인데요.

asyncio를 기반으로 개발된 가볍고 효율적인 웹 서버 프레임워크입니다.

Uvicorn은 처음 설계될 때 두 가지 목표를 달성하는 것을 목표로 했습니다.

첫 번째는 uvloop와 httptools를 사용하여 매우 빠른 asyncio 서버를 구현하는 것이었고, 두 번째는 ASGI를 기반으로 최소한의 애플리케이션 인터페이스를 구현하는 것이었습니다.

현재 HTTP, 웹소켓, Pub/Sub 브로드캐스트를 지원하며 다른 프로토콜 및 메시지 유형으로 확장할 수 있습니다.

공식 웹사이트: uvicorn (https://uvicorn.org/)


uvloop와 httptools는 무엇일까요?

답변: uvloop는 표준 라이브러리 asyncio의 이벤트 루프를 대체하는 데 사용됩니다.

Cython으로 구현되어 매우 빠르며, asyncio의 속도를 2~4배까지 향상시킬 수 있습니다.

비동기 코드를 작성하는 데 필수적인 asyncio에 익숙하실 거라고 생각합니다.

httptools는 Node.js HTTP 파서의 Python 구현입니다.

 

ASGI 서버는 무엇일까요?

답변: ASGI(Asynchronous Server Gateway Interface)는 네트워크 프로토콜 서비스와 Python 애플리케이션 간의 표준 인터페이스입니다.

HTTP, HTTP2, WebSocket을 포함한 여러 일반적인 프로토콜 유형을 처리할 수 있습니다.

ASGI 프로토콜: https://asgi.readthedocs.io/en/latest/specs/main.html


Uvicorn을 간략하게 소개합니다.

답변: 현재 Python에는 비동기 게이트웨이 프로토콜 인터페이스가 부족합니다.

ASGI의 등장이 이러한 격차를 메워주고 있는데요.

이제부터는 공통 표준을 사용하여 모든 비동기 프레임워크를 위한 도구를 구현할 수 있습니다.

ASGI는 Python이 웹 프레임워크에서 Node.JS 및 Golang과 경쟁할 수 있도록 고성능 IO 집약적인 작업을 목표로 합니다.

ASGI는 HTTP2 및 WebSockets를 지원하는 반면, WSGI는 지원하지 않습니다.

Uvicorn은 현재 HTTP1.1 및 WebSocket을 지원하며 HTTP2 지원을 계획하고 있습니다.

 

Uvicorn 사용법

 

설치: pip install uvicorn을 실행합니다.

새로운 example.py 파일 생성

async def app(scope, receive, send):
    assert scope['type'] == 'http'
    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [
            [b'content-type', b'text/plain'],
        ]
    })
    await send({
        'type': 'http.response.body',
        'body': b'Hello, world!',
    })

 

명령줄에서 Uvicorn 시작: uvicorn example:app을 실행합니다.

스크립트 형태로 시작

import uvicorn

async def app(scope, receive, send):
   ...

if __name__ == "__main__":
    uvicorn.run("example:app", host="127.0.0.1", port=8000, log_level="info")

 

Uvicorn은 여러 명령을 지원하며, uvicorn --help로 확인할 수 있습니다.

➜  ~ uvicorn --help
Usage: uvicorn [OPTIONS] APP

Options:
  --host TEXT                     소켓을 이 호스트에 바인딩합니다. [기본값:
                                  127.0.0.1]
  --port INTEGER                  소켓을 이 포트에 바인딩합니다. 0인 경우 사용 가능한
                                  포트를 선택합니다. [기본값: 8000]
  --uds TEXT                      UNIX 도메인 소켓에 바인딩합니다.
  --fd INTEGER                    이 파일 설명자에서 소켓에 바인딩합니다.
  --reload                        자동 재시작을 활성화합니다.
  --reload-dir PATH               현재 작업 디렉토리 대신 명시적으로 재시작 디렉토리를 설정합니다.
  --reload-include TEXT           파일을 감시할 때 포함할 glob 패턴을 설정합니다. 기본적으로
                                  '*.py'가 포함됩니다. 이러한 기본값은 `--reload-
                                  exclude`로 재정의할 수 있습니다. 이 옵션은 watchfiles가
                                  설치된 경우에만 효과가 있습니다.
  --reload-exclude TEXT           파일을 감시할 때 제외할 glob 패턴을 설정합니다. 기본적으로
                                  '.*, .py[cod], .sw.*, ~*'가 포함됩니다. 이러한 기본값은
                                  `--reload-include`로 재정의할 수 있습니다. 이 옵션은
                                  watchfiles가 설치된 경우에만 효과가 있습니다.
  --reload-delay FLOAT            애플리케이션이 다시 로드되어야 하는지 확인하기까지의 지연 시간입니다.
                                  기본값은 0.25초입니다. [기본값: 0.25]
  --workers INTEGER               작업 프로세스 수입니다. 사용 가능한 경우 $WEB_CONCURRENCY
                                  환경 변수의 기본값을 사용하거나 1로 설정합니다. --reload와
                                  함께 사용할 수 없습니다.
  --loop [auto|asyncio|uvloop]    이벤트 루프 구현입니다. [기본값: auto]
  --http [auto|h11|httptools]     HTTP 프로토콜 구현입니다. [기본값: auto]
  --ws [auto|none|websockets|wsproto]
                                  WebSocket 프로토콜 구현입니다. [기본값: auto]
  --ws-max-size INTEGER           WebSocket 최대 크기 메시지(바이트) [기본값: 16777216]
  --ws-max-queue INTEGER          WebSocket 메시지 대기열의 최대 길이입니다. [기본값: 32]
  --ws-ping-interval FLOAT        WebSocket 핑 간격(초) [기본값: 20.0]
  --ws-ping-timeout FLOAT         WebSocket 핑 시간 초과(초) [기본값: 20.0]
  --ws-per-message-deflate BOOLEAN
                                  WebSocket 메시지당 deflate 압축 [기본값: True]
  --lifespan [auto|on|off]        수명 구현입니다. [기본값: auto]
  --interface [auto|asgi3|asgi2|wsgi]
                                  애플리케이션 인터페이스로 ASGI3, ASGI2 또는 WSGI를 선택합니다.
                                  [기본값: auto]
  --env-file PATH                 환경 구성 파일입니다.
  --log-config PATH               로깅 구성 파일입니다. 지원되는 형식: .ini, .json, .yaml.
  --log-level [critical|error|warning|info|debug|trace]
                                  로그 수준입니다. [기본값: info]
  --access-log / --no-access-log  액세스 로그를 활성화/비활성화합니다.
  --use-colors / --no-use-colors  컬러 로그를 활성화/비활성화합니다.
  --proxy-headers / --no-proxy-headers
                                  X-Forwarded-Proto, X-Forwarded-For, X-Forwarded-Port를
                                  활성화/비활성화하여 원격 주소 정보를 채웁니다.
  --server-header / --no-server-header
                                  기본 서버 헤더를 활성화/비활성화합니다.
  --date-header / --no-date-header
                                  기본 날짜 헤더를 활성화/비활성화합니다.
  --forwarded-allow-ips TEXT      프록시 헤더와 함께 신뢰할 IP 주소 목록(쉼표로 구분)입니다.
                                  사용 가능한 경우 $FORWARDED_ALLOW_IPS 환경 변수의 기본값을
                                  사용하거나 '127.0.0.1'로 설정합니다.
  --root-path TEXT                지정된 URL 경로 아래에 서브마운트된 애플리케이션의 ASGI 'root_path'를 설정합니다.
  --limit-concurrency INTEGER     동시 연결 또는 작업의 최대 수입니다. 이 수를 초과하면 HTTP 503
                                  응답이 발생합니다.
  --backlog INTEGER               백로그에 보관할 최대 연결 수입니다.
  --limit-max-requests INTEGER    프로세스를 종료하기 전에 처리할 최대 요청 수입니다.
  --timeout-keep-alive INTEGER    새 데이터가 수신되지 않으면 Keep-Alive 연결을 닫는 시간(초)입니다.
                                  [기본값: 5]
  --timeout-graceful-shutdown INTEGER
                                  정상 종료를 기다리는 최대 시간(초)입니다.
  --ssl-keyfile TEXT              SSL 키 파일입니다.
  --ssl-certfile TEXT             SSL 인증서 파일입니다.
  --ssl-keyfile-password TEXT     SSL 키 파일 암호입니다.
  --ssl-version INTEGER           사용할 SSL 버전(stdlib ssl 모듈 참조) [기본값: 17]
  --ssl-cert-reqs INTEGER         클라이언트 인증서가 필요한지 여부(stdlib ssl 모듈 참조)
                                  [기본값: 0]
  --ssl-ca-certs TEXT             CA 인증서 파일입니다.
  --ssl-ciphers TEXT              사용할 암호(stdlib ssl 모듈 참조) [기본값: TLSv1]
  --header TEXT                   사용자 지정 기본 HTTP 응답 헤더를 Name:Value 쌍으로 지정합니다.
  --version                       uvicorn 버전을 표시하고 종료합니다.
  --app-dir TEXT                  APP를 지정된 디렉터리에서 찾습니다. PYTHONPATH에 추가합니다.
                                  기본값은 현재 작업 디렉터리입니다.
  --h11-max-incomplete-event-size INTEGER
                                  h11의 경우 불완전한 이벤트의 최대 버퍼 크기(바이트)입니다.
  --factory                       APP를 애플리케이션 팩토리, 즉 () -> <ASGI 앱> 호출 가능한 것으로 처리합니다.
  --help                          이 메시지를 표시하고 종료합니다.

 

구성 및 서버 인스턴스

구성 및 서버 수명 주기를 더 잘 제어하려면 uvicorn.Configuvicorn.Server를 사용합니다.

import uvicorn

async def app(scope, receive, send):
   ...

if __name__ == "__main__":
    config = uvicorn.Config("main:app", port=5000, log_level="info")
    server = uvicorn.Server(config)
    server.run()

 

이미 실행 중인 비동기 환경에서 Uvicorn을 실행하려면 uvicorn.Server.serve()를 사용합니다.

import asyncio
import uvicorn

async def app(scope, receive, send):
   ...

async def main():
    config = uvicorn.Config("main:app", port=5000, log_level="info")
    server = uvicorn.Server(config)
    await server.serve()

if __name__ == "__main__":
    asyncio.run(main())

 

FastAPI 프로젝트를 Uvicorn으로 시작하기

import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

if __name__ == '__main__':
    uvicorn.run(app=app)

 

FastAPI가 Uvicorn을 사용하는 이유는 무엇일까요?

FastAPI는 현대적이고 고성능 웹 프레임워크입니다.

Python의 비동기 프로그래밍 기능을 사용하여 웹 애플리케이션의 성능을 향상시키는데요.

반면 Uvicorn은 uvloop와 httptools로 구현된 고성능 ASGI 서버로, HTTP 요청을 비동기적으로 처리할 수 있습니다.

FastAPI는 Uvicorn이 매우 빠르고 안정적이며 사용하기 쉬워서 기본 웹 서버로 사용합니다.

많은 동시 연결을 처리할 때 안정적이고 효율적으로 유지될 수 있습니다.

또한 Uvicorn은 WebSocket 및 HTTP/2와 같은 새로운 기능을 지원하며, 이는 FastAPI가 옹호하는 최신 웹 개발 철학과 일치합니다.

따라서 Uvicorn을 FastAPI의 웹 서버로 사용하는 것은 훌륭한 선택입니다.