728x90

FastAPI 개발자 tiangolo는 FastAPI를 어떻게 사용할까 궁금해서 찾아본 프로젝트

https://github.com/tiangolo/full-stack-fastapi-postgresql

이번 게시글에서는 프로젝트 설정파일인 project.toml을 분석하려고 함

 

pyproject.toml 설명

[tool.poetry]
name = "app"
version = "0.1.0"
description = ""
authors = ["Admin <admin@example.com>"]

[tool.poetry.dependencies]
python = "^3.10"
uvicorn = "^0.24.0.post1"
fastapi = "^0.104.1"
python-multipart = "^0.0.6"
email-validator = "^2.1.0.post1"
celery = "^5.3.5"
passlib = {extras = ["bcrypt"], version = "^1.7.4"}
tenacity = "^8.2.3"
pydantic = ">2.0"
emails = "^0.6"
gunicorn = "^21.2.0"
jinja2 = "^3.1.2"
alembic = "^1.12.1"
python-jose = {extras = ["cryptography"], version = "^3.3.0"}
httpx = "^0.25.1"
psycopg = {extras = ["binary"], version = "^3.1.13"}
sqlmodel = "^0.0.16"
# Pin bcrypt until passlib supports the latest
bcrypt = "4.0.1"
pydantic-settings = "^2.2.1"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
pytest-cov = "^4.1.0"
mypy = "^1.8.0"
ruff = "^0.2.2"
pre-commit = "^3.6.2"

[tool.isort]
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
line_length = 88

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

[tool.mypy]
strict = true

[tool.ruff]
target-version = "py310"

[tool.ruff.lint]
select = [
    "E",  # pycodestyle errors
    "W",  # pycodestyle warnings
    "F",  # pyflakes
    "I",  # isort
    "B",  # flake8-bugbear
    "C4",  # flake8-comprehensions
    "UP",  # pyupgrade
]
ignore = [
    "E501",  # line too long, handled by black
    "B008",  # do not perform function calls in argument defaults
    "W191",  # indentation contains tabs
    "B904",  # Allow raising exceptions without from e, for HTTPException
]

[tool.ruff.lint.pyupgrade]
# Preserve types, even if a file imports `from __future__ import annotations`.
keep-runtime-typing = true

 

  • [tool.poetry] 섹션
    • 프로젝트의 기본 정보를 정의
    • name: 프로젝트의 이름
    • version: 프로젝트의 버전
    • description: 프로젝트에 대한 간단한 설명
    • authors: 프로젝트 저자들의 이메일 주소를 포함한 리스트
  • [tool.poetry.dependencies] 섹션
    • 여러 Python 라이브러리와 프레임워크에 대한 의존성을 정의 (requirements.txt와 유사한 역할)
    • python: 사용할 Python 버전
    • uvicorn, fastapi, python-multipart, ...: FastAPI와 관련된 라이브러리 및 도구들
    • pydantic: 데이터 유효성 검사 및 설정을 위한 라이브러리
    • gunicorn, jinja2, alembic, ...: 다양한 라이브러리 및 도구들
    • bcrypt: 비밀번호 해싱을 위한 라이브러리
    • pydantic-settings: Pydantic 설정을 관리하기 위한 도구
  • [tool.isort] 섹션
    • 코드를 정렬하기 위한 isort 설정
    • 참고 : isort는 Python 코드 파일의 import 문을 정렬해주는 도구
  • [build-system] 섹션
    • 프로젝트 빌드에 사용되는 도구 및 설정
  • [tool.mypy] 섹션
    • mypy 정적 타입 검사 도구의 설정
    • 참고 : mypy는 Python 코드의 정적 타입 검사를 수행하는 도구로 런타임 이전에 코드를 분석하여 타입 오류를 찾아내고 이를 사전에 방지
  • [tool.ruff] 섹션
    • ruff 코드 포맷터 및 정적 분석 도구의 설정
    • 참고 : ruff는 rust로 만든 매우 빠른 Python linter
    • 참고 : linter는 코드의 오류나 버그가 있는지 확인하고 정해진 규칙을 잘 지키고 있는지에 대한 것들을 개발하면서 확인 및 점검을 하기 위해 사용하는 도구

 

pyproject.toml을 이용한 poetry 가상환경 설정

pyproject.toml이 있는 경우 poetry를 이용하여 간단하게 가상환경을 세팅할 수 있음

pip install --upgrade pip
pip install poetry
poetry install
poetry shell

 

가상환경을 세팅한 후에는 vscode에서 F1Python: Select Interpreter 를 클릭하여 poetry 가상환경 사용 가능

 

참고 : FastAPI 개발자가 직접 개발한 FastAPI backend 시리즈

FastAPI 개발자가 직접 개발한 FastAPI backend 프로젝트 구조

FastAPI 개발자가 직접 개발한 FastAPI backend alembic

FastAPI 개발자가 직접 개발한 FastAPI backend DB 초기화 방법

FastAPI 개발자가 직접 개발한 FastAPI backend pyproject.toml

FastAPI 개발자가 직접 개발한 FastAPI backend 설정파일 config.py

FastAPI 개발자가 직접 개발한 FastAPI backend 데이터모델 models.py

 

728x90
728x90

FastAPI 개발자 tiangolo는 FastAPI를 어떻게 사용할까 궁금해서 찾아본 프로젝트

https://github.com/tiangolo/full-stack-fastapi-postgresql

 

FastAPI(백엔드)의 기본적인 프로젝트 구조는 아래와 같음

```

app
├── alembic # 데이터베이스 마이그레이션을 관리하는 Alembic 설정 파일 및 스크립트가 저장된 디렉토리
├── api # FastAPI 애플리케이션의 API 엔드포인트 및 라우터가 정의된 디렉토리
├── core # 애플리케이션의 핵심 기능 및 설정이 정의된 모듈이 위치하는 디렉토리
├── schemas # Pydantic 스키마 정의가 포함된 디렉토리로, 데이터의 유효성 검사와 API 요청 및 응답의 구조를 정의
├── email-templates # 이메일 템플릿 파일이 저장된 디렉토리
├── tests # 테스트 파일이 위치한 디렉토리
├── __init__.py # Python에서 패키지로 인식되도록 하는 빈 __init__.py 파일
├── backend_pre_start.py # FastAPI 애플리케이션 시작 전에 실행할 코드가 정의된 파일
├── celeryworker_pre_start.py # Celery worker 시작 전에 실행할 코드가 정의된 파일
├── initial_data.py # 애플리케이션 초기 데이터를 설정하는 스크립트 파일
├── tests_pre_start.py # 테스트 시작 전에 실행할 코드가 정의된 파일
├── main.py # FastAPI 애플리케이션의 진입점이 되는 파일로, API 라우터를 구성하고 애플리케이션을 실행
├── models.py # SQLAlchemy 모델이 정의된 파일
├── crud.py # 데이터베이스 CRUD(Create, Read, Update, Delete) 연산을 수행하는 함수가 정의된 파일
├── utils.py # 애플리케이션에서 사용되는 유틸리티 함수가 정의된 파일
└── worker.py # Celery worker의 작업을 정의하는 파일

pyproject.toml # Poetry 프로젝트의 설정 파일로, 의존성 및 프로젝트 메타데이터를 정의
.env # 환경 변수 설정이 담긴 파일
.dockerignore # Docker 이미지 빌드 시 제외할 파일 및 디렉토리를 지정하는 파일
.gitignore # Git으로 추적하지 않을 파일 및 디렉토리를 지정하는 파일
alembic.ini # Alembic의 설정 파일로, 데이터베이스 마이그레이션 설정을 정의 # 
backend.dockerfile # FastAPI 애플리케이션을 위한 Docker 이미지를 빌드하는 데 사용되는 Docker 파일
celeryworker.dockerfile # Celery worker를 위한 Docker 이미지를 빌드하는 데 사용되는 Docker 파일
prestart.sh # FastAPI 애플리케이션 시작 전에 실행할 스크립트 파일
tests-start.sh # 테스트 시작 시 실행할 스크립트 파일
worker-start.sh # Celery worker 시작 시 실행할 스크립트 파일

```

 

참고 : FastAPI 개발자가 직접 개발한 FastAPI backend 시리즈

FastAPI 개발자가 직접 개발한 FastAPI backend 프로젝트 구조

FastAPI 개발자가 직접 개발한 FastAPI backend alembic

FastAPI 개발자가 직접 개발한 FastAPI backend DB 초기화 방법

FastAPI 개발자가 직접 개발한 FastAPI backend pyproject.toml

FastAPI 개발자가 직접 개발한 FastAPI backend 설정파일 config.py

FastAPI 개발자가 직접 개발한 FastAPI backend 데이터모델 models.py

 

728x90
728x90

git fork 한 프로젝트에 pyproject.toml이 있는 경우 poetry를 이용하여 간단하게 가상환경을 세팅할 수 있음

pip install --upgrade pip
pip install poetry
poetry install
poetry shell

 

 

poetry 사용법

728x90

'Python' 카테고리의 다른 글

RuntimeError: Failed to lock Pipfile.lock!  (0) 2024.03.05
alembic 소개 및 기초 사용법  (0) 2024.03.05
pyproject.toml 이란?  (0) 2024.03.03
객체지향 설계 5원칙 (SOLID)  (0) 2024.03.02
파이썬 객체지향 프로그래밍  (0) 2024.03.02
728x90

pyproject.toml이란?

파이썬 프로젝트의 표준 파일로, PEP 518PEP 621에 따라 정의됨

pyproject.toml는 프로젝트의 메타데이터 및 빌드에 필요한 도구들의 설정을 포함하는 설정파일

pyproject.toml 등장 배경

setuptools는 파이썬 프로젝트를 패키징하는 라이브러리로 널리 사용되었음

setuptools는 설정파일로 setup.py를 사용하는데, setuptools에 종속적임

setuptools가 파이썬 표준 라이브러리가 아니고, 다른 여러 빌드 시스템이 존재하기 때문에 개선할 필요성이 있었음

 

이를 개선하기 위해 특정 빌드 시스템에 종속되지 않는 선언적인 설정 파일 pyproject.toml이 제안됨

setup.py 대신 pyproject.toml을 사용한다는 것은 setuptools라는 특정한 라이브러리에 파이썬 패키징 시스템이 종속되지 않게 된다는 것을 의미함

pyproject.toml의 빌드시스템 설정파일로 사용하는 방법

pyproject.toml은 TOML 포맷으로 만들어진 설정파일

파이썬 패키지를 어떻게 빌드하는지, 어떤 빌드 시스템을 사용해야 하는지를 명시

 

<setuptools를 빌드시스템으로 사용하는 pyproject.toml 예시>

# pyproject.toml 예시
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

 

<flit을 빌드시스템으로 사용하는 pyproject.toml 예시>

[build-system]
requires = ["flit_core"]
build-backend = "flit_core.buildapi"

pyproject.toml에 그 외 설정을 저장하는 방법

개발과 관련된 설정 값을 관리하는 용도로도 pyproject.toml을 사용할 수 있음

빌드 전에 수행되어야 하는 테스트, 코드 포맷팅 등에 대한 정보를 pyproject.toml에 함께 적으면 유용함

 

<코드 포맷팅 도구 black, 테스트용 프레임워크 pytest의 설정값을  pyproject.toml에 저장하는 예시>

# pyproject.toml of black
[tool.black]
line-length = 88
target-version = ['py37', 'py38', 'py39', 'py310']
include = '\.pyi?$'
exclude = '''
/(
  \.git
  | \.mypy_cache
  | \.venv
  | _build
  | build
  | dist
)/
'''

# pyproject.toml of pytest
[tool.pytest.ini_options]
minversion = "6.0"
addopts = "-ra -q"
testpaths = ["tests"]

참고

- 파이썬 패키징의 과거, 현재, 그리고 미래

728x90
728x90

SRP (Single Responsibility Principle) : 단일 책임 원칙

하나의 클래스는 하나의 책임만 가져야 함

## 단일 책임 원칙을 준수하지 않은 예시
class User:
    def __init__(self, username, email):
        self.username = username
        self.email = email

    def save_to_database(self):
        # 데이터베이스에 저장하는 작업

    def send_email(self, message):
        # 이메일 보내는 작업

 

## 단일책임원칙을 준수하도록 수정
class User:
    def __init__(self, username, email):
        self.username = username
        self.email = email

class UserRepository:
    def save_to_database(self, user):
        # 데이터베이스에 저장하는 작업

class EmailService:
    def send_email(self, user, message):
        # 이메일 보내는 작업

OCP (Open-Closed Principle) : 개방 폐쇄 원칙

소프트웨어의 구성 요소(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 함

## 개방/폐쇄 원칙을 위반한 예시
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

class AreaCalculator:
    def calculate_area(self, rectangle):
        return rectangle.width * rectangle.height

 

## 개방/폐쇄 원칙을 준수하도록 수정
# 추상 클래스를 이용한 확장 가능한 디자인
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius ** 2

LSP (Liskov Substitution Principle) : 리스 코프 치환 원칙

자식클래스는 언제나 부모클래스를 대체할 수 있어야 함

## 리스코프 치환 원칙을 위반한 예시
class Bird:
    def fly(self):
        pass

class Penguin(Bird):
    def fly(self):
        raise Exception("Penguins can't fly!")

def make_bird_fly(bird):
    bird.fly()

 

## 리스코프 치환 원칙을 준수하도록 수정
# 추상 클래스를 사용하여 공통된 인터페이스 정의
from abc import ABC, abstractmethod

class Bird(ABC):
    @abstractmethod
    def fly(self):
        pass

class Sparrow(Bird):
    def fly(self):
        print("Sparrow is flying")

class Penguin(Bird):
    def fly(self):
        print("Penguin can't fly")

def make_bird_fly(bird):
    bird.fly()

ISP (Interface Segragation Principle) : 인터페이스 분리 원칙

클라이언트는 자신이 사용하지 않는 메서드에 의존해서는 안됨

## 인터페이스 분리 원칙을 위반한 예시
class Worker:
    def work(self):
        pass

    def eat(self):
        pass

class SuperWorker(Worker):
    def work(self):
        pass

    def eat(self):
        pass

 

## 인터페이스 분리 원칙을 준수하도록 수정
# 인터페이스를 분리하여 클라이언트가 필요한 메서드만 사용
class Workable(ABC):
    @abstractmethod
    def work(self):
        pass

class Eatable(ABC):
    @abstractmethod
    def eat(self):
        pass

class Worker(Workable, Eatable):
    def work(self):
        pass

    def eat(self):
        pass

class SuperWorker(Workable, Eatable):
    def work(self):
        pass

    def eat(self):
        pass

DIP (Dependency Inversion Principle) : 의존 관계 역전 원칙

고수준 모듈은 저수준 모듈에 의존해서는 안되며, 둘 모두 추상화에 의존해야 함

추상화된 것에 의존하면 구체적인 구현 사항이 변경되어도 영향을 받지 않게 됨

## 의존 역전 원칙을 위반한 예시
class LightBulb:
    def turn_on(self):
        print("LightBulb: On")

    def turn_off(self):
        print("LightBulb: Off")

class Switch:
    def __init__(self, bulb):
        self.bulb = bulb

    def operate(self):
        self.bulb.turn_on()

bulb = LightBulb()
switch = Switch(bulb)
switch.operate()

 

## 의존 역전 원칙을 준수하도록 수정
# 추상화를 통해 의존성을 역전시킴
from abc import ABC, abstractmethod

class Switchable(ABC):
    @abstractmethod
    def turn_on(self):
        pass

    @abstractmethod
    def turn_off(self):
        pass

class LightBulb(Switchable):
    def turn_on(self):
        print("LightBulb: On")

    def turn_off(self):
        print("LightBulb: Off")

class Switch:
    def __init__(self, device):
        self.device = device

    def operate(self):
        self.device.turn_on()

bulb = LightBulb()
switch = Switch(bulb)
switch.operate()
728x90
728x90

프로그램 설계방법론의 일종으로, 명령형 프로그래밍에 속함

프로그램을 수많은 객체라는 기본 단위로 나누고, 이들의 상호작용으로 서술하는 방식

객체지향 프로그래밍을 사용하면 코드를 모듈화하고 유지보수하기 쉬움

클래스와 객체

클래스는 비슷한 특성을 가진 객체를 만드기 위한 일종의 틀

객체는 클래스를 기반으로 생성된 instance

객체는 "데이터"와 데이터를 처리하는 "메서드(함수)"를 포함

캡슐화

객체 내부의 데이터와 메서드는 외부에서 직접 접근하지 못하도록 제한되고, 필요한 경우에만 공개된 인터페이스를 통해 상호작용할 수 있도록 하는 원칙

class BankAccount:
    def __init__(self, balance=0):
        self._balance = balance  # _balance는 보호된 속성

    def deposit(self, amount):
        self._balance += amount

    def withdraw(self, amount):
        if amount <= self._balance:
            self._balance -= amount
        else:
            print("Insufficient funds")

    def get_balance(self):
        return self._balance

상속

클래스 간에 관계를 설정하여 기존 클래스의 특성과 동작을 재사용하고, 새로운 클래스를 정의
코드의 재사용성이 향상됨

class Animal:
    def __init__(self, name):
        self.name = name

    def make_sound(self):
        pass  # 하위 클래스에서 구현할 메서드

class Dog(Animal):
    def make_sound(self):
        return "Woof!"

class Cat(Animal):
    def make_sound(self):
        return "Meow!"

my_dog = Dog("Buddy")
my_cat = Cat("Whiskers")

print(my_dog.make_sound())  # 출력: Woof!
print(my_cat.make_sound())  # 출력: Meow!

다형성

같은 이름의 메서드를 각 객체에서 다르게 구현될 수 있는 특성
코드의 유연성을 증가시킴

class Shape:
    def area(self):
        pass  # 하위 클래스에서 구현할 메서드

class Square(Shape):
    def __init__(self, side_length):
        self.side_length = side_length

    def area(self):
        return self.side_length ** 2

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius ** 2

square = Square(5)
circle = Circle(3)

print(square.area())  # 출력: 25
print(circle.area())  # 출력: 28.26

후속

파이썬 객체지향 5원칙

728x90
728x90

WSGI(웹 서버 게이트웨이 인터페이스)와 ASGI(비동기 서버 게이트웨이 인터페이스)는 웹 애플리케이션 서버와 웹 애플리케이션 간의 표준화된 인터페이스를 제공하는 목적으로 사용되는 두 가지 프로토콜

 

WSGI는 주로 동기적인 애플리케이션에 사용됨

ASGI는 비동기적이며 실시간 기능이 필요한 애플리케이션에 사용하며, ASGI는 특히 웹 소켓을 이용한 양방향 통신과 같은 기능을 지원하여 실시간 애플리케이션을 개발할 때 유용함

 

WSGI (Web Server Gateway Interface):

  1. 동기적인 처리: WSGI는 동기적인 방식으로 동작하며, 요청이 처리될 때까지 블록 됨 (하나의 요청이 완전히 처리되기 전에 다른 요청을 처리하지 못하고 대기)
  2. 대부분의 경우에 적합: WSGI는 대부분의 웹 애플리케이션에 적합하며, 간단한 애플리케이션에서 잘 동작함
  3. 웹 서버와의 통합 용이성: WSGI 애플리케이션은 여러 웹 서버와 쉽게 통합될 수 있음
  4. 플라스크 프레임워크에서 사용

ASGI (Asynchronous Server Gateway Interface):

  1. 비동기적인 처리: ASGI는 비동기적으로 동작하며, 여러 요청을 동시에 처리 가능 → 실시간 기능이 필요한 애플리케이션에 적합
  2. 실시간 기능 및 이벤트 처리: ASGI는 웹 소켓을 비롯한 실시간 기능 및 이벤트 기능을 지원하여 실시간 채팅, 게임 등과 같은 애플리케이션에 유용함
  3. Django, FastAPI와 같은 프레임워크에서 비동기적으로 동작하는 애플리케이션을 지원하는 데 사용
728x90
728x90

1. 동기

작업이 순차적으로 실행됨

 

2. threading (동기, i/o-bound 병렬처리)

I/O bound 동기 작업을 병렬 처리하는 데에 사용

스레드는 별도의 실행 흐름을 생성하고 여러 스레드를 사용하여 동시에 여러 작업을 수행

I/O 바운드 작업에서는 효과적으로 동작할 수 있지만, CPU 바운드 작업에서는 GIL(Global Interpreter Lock)로 인해 병렬성의 이점을 얻기 어려움 (프로세스 하나, 메모리 공유)

 

3. asyncio.gather (비동기, i/o-bound 병렬처리)

I/O bound 비동기 작업을 병렬 처리하는 데에 사용

하나의 이벤트 루프 내에서 여러 작업이 비동기적으로 실행

각 작업이 I/O 바운드 작업이라면 이벤트 루프가 대기 중인 작업을 계속해서 실행 가능

비동기 작업은 비동기 함수 안에서 await를 통해 비동기적으로 실행되기 때문에, 메모리 공유 측면에서는 주로 이벤트 루프 내에서 발생하므로 스레딩보다는 적은 동시 액세스가 발생

I/O 바운드 작업에서는 효과적으로 동작할 수 있지만, CPU 바운드 작업에서는 GIL(Global Interpreter Lock)로 인해 병렬성의 이점을 얻기 어려움 (프로세스 하나, 메모리 공유)

 

4. multiprocessing (cpu-bound 병렬처리)

CPU bound 작업을 병렬처리하는 데 사용

각 프로세스는 독립적인 메모리 공간을 가지고 있어 GIL(Global Interpreter Lock)의 영향을 받지 않아 CPU 바운드 작업에서 높은 병렬성을 얻을 수 있음

프로세스 간 통신이나 데이터 공유는 복잡하고 오버헤드가 큼

 

 

728x90
728x90

소스코드

import time
import asyncio
from fastapi import FastAPI

app = FastAPI()

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

# FastAPI는 async를 사용하지 않아도 비동기로 작업을 수행
# https://tigris-data-science.tistory.com/entry/FastAPI%EC%9D%98-%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D
@app.get("/sleep") # API처리중에 다른API 처리가능
def time_sleep():
    time.sleep(5)
    return 5

# async를 사용하면 메인쓰레드에서 동작하여 cpu-bound 작업 수행 시 다른 쓰레드 Blocking됨
@app.get("/async-sleep") # API처리 끝날때까지 다른API block
async def async_sleep():
    time.sleep(5)
    return 5

# async를 사용할 땐 메모리 bound작업을 해야 다른 쓰레드가 Blocking되지 않음
@app.get("/async-asyncio-sleep") # API처리중에 다른API 처리가능
async def asyncio_sleep():
    await asyncio.sleep(5)
    return 5

참고 : 개발환경 라이브러리 버전

annotated-types==0.6.0
anyio==4.3.0
click==8.1.7
colorama==0.4.6
fastapi==0.110.0
h11==0.14.0
idna==3.6
pydantic==2.6.2
pydantic_core==2.16.3
sniffio==1.3.1
starlette==0.36.3
typing_extensions==4.10.0
uvicorn==0.27.1

 

728x90
728x90

소스코드

from fastapi import FastAPI
import time
import asyncio
from datetime import datetime

app = FastAPI()

sem = asyncio.Semaphore(3) # 최대 동시처리 가능 개수 제한

@app.get("/")
def root():
    print("/", datetime.now())
    return {"date_time": datetime.now()}

@app.get("/time-sleep")
def time_sleep():
    start_time = datetime.now()
    results = time.sleep(6)
    end_time = datetime.now()
    return {"start_time":start_time, "end_time":end_time, "results":results}


@app.get("/async")
async def async_root():
    print("/async", datetime.now())
    return {"date_time": datetime.now()}

async def _async_sleep(num, sec): # time sleep 은 I/O bound 작업을 테스트할 때 사용
    async with sem:
        print("start sleep", datetime.now())
        await asyncio.sleep(sec)
        print("end sleep", datetime.now())
        return f"hehe {num}"

async def task_in_parallel():
    # 여러 개의 비동기 작업을 동시에 실행
    results = await asyncio.gather( 
        asyncio.create_task(_async_sleep(1,6)),
        asyncio.create_task(_async_sleep(2,6)),
        asyncio.create_task(_async_sleep(3,6)),
        asyncio.create_task(_async_sleep(4,6)),
        asyncio.create_task(_async_sleep(5,6)),
    )
    return results
    
@app.get("/async-asyncio-sleep")
async def parallel_sleep():
    start_time = datetime.now()
    results = await task_in_parallel()
    end_time = datetime.now()
    return {"start_time":start_time, "end_time":end_time, "results":results}

@app.get("/async-sleep")
async def async_sleep():
    start_time = datetime.now()
    results = await _async_sleep(1,6)
    end_time = datetime.now()
    return {"start_time":start_time, "end_time":end_time, "results":results}


@app.get("/asyncio-sleep")
async def asyncio_sleep():
    start_time = datetime.now()
    results = await asyncio.sleep(6)
    end_time = datetime.now()
    return {"start_time":start_time, "end_time":end_time, "results":results}

 

  • 앱 띄우는 방법 : uvicorn main:app --port 8000
  • 브라우저에서 스웨거 접속하는 방법 : http://localhost:8000/docs
  • 동시처리 API 응답 : async-asyncio-sleep API 호출 시 6초짜리 time sleep 함수 5개를 3개 / 2개 비동기처리하여 약 12초만에 응답 받음
{
  "start_time": "2024-02-26T22:36:15.098960",
  "end_time": "2024-02-26T22:36:27.101120",
  "results": [
    "hehe 1",
    "hehe 2",
    "hehe 3",
    "hehe 4",
    "hehe 5"
  ]
}

 

  • 비동기처리 앱 로그 : async-asyncio-sleep API 처리 중 다른 API 요청 처리됨
INFO:     Application startup complete.
start sleep 2024-02-26 22:46:12.735016
start sleep 2024-02-26 22:46:12.735016
start sleep 2024-02-26 22:46:12.736013
/async 2024-02-26 22:46:14.222185
INFO:     127.0.0.1:55682 - "GET /async HTTP/1.1" 200 OK
/ 2024-02-26 22:46:16.353565
INFO:     127.0.0.1:55682 - "GET / HTTP/1.1" 200 OK
end sleep 2024-02-26 22:46:18.739985
end sleep 2024-02-26 22:46:18.740557
end sleep 2024-02-26 22:46:18.741149
start sleep 2024-02-26 22:46:18.741675
start sleep 2024-02-26 22:46:18.741675
/async 2024-02-26 22:46:20.443945
INFO:     127.0.0.1:55682 - "GET /async HTTP/1.1" 200 OK
/ 2024-02-26 22:46:23.335636
INFO:     127.0.0.1:55682 - "GET / HTTP/1.1" 200 OK
end sleep 2024-02-26 22:46:24.738253
end sleep 2024-02-26 22:46:24.738253
INFO:     127.0.0.1:55681 - "GET /async-asyncio-sleep HTTP/1.1" 200 OK

 

참고 : 개발환경 라이브러리 버전

annotated-types==0.6.0
anyio==4.3.0
click==8.1.7
colorama==0.4.6
fastapi==0.110.0
h11==0.14.0
idna==3.6
pydantic==2.6.2
pydantic_core==2.16.3
sniffio==1.3.1
starlette==0.36.3
typing_extensions==4.10.0
uvicorn==0.27.1
728x90

'Python > Web' 카테고리의 다른 글

FastAPI 개발자가 직접 개발한 FastAPI backend 프로젝트 구조  (0) 2024.03.03
WSGI와 ASGI 단순 비교  (0) 2024.02.28
FastAPI 비동기 처리  (1) 2024.02.26
[windows] nginx 프록시 설정 방법  (0) 2022.11.01
nginx 기초  (0) 2022.10.25

+ Recent posts