Рубрики
Без рубрики

Micreervice в Python с помощью Fastapi

Создание микросервисов с помощью Python Invection в MiceraServices Преимущества Microse … Помечено Python, Fastapi, Restapi, микросервисы.

  • Введение в микросервисы
    • Преимущества микросервиса
    • Недостатки микросервиса
    • Почему микросервис в Python
  • Введение в Fastapi.
    • Почему fastapi.
    • Установка fastapi.
    • Создание простого API отдыха с помощью Fastapi
    • Использование PostgreSQL базы данных с fastapi
  • Шаблоны управления данными Micreervice
    • База данных за услугу
    • Общая база данных
    • Композиция API.
  • Создание Python Micreervice в Docker
    • Установка Docker и Docker Compose
    • Создание фильмов Service.
    • Создание Chasts Service
    • Запуск микросервиса с помощью Docker Compose
    • Использование Nginx для доступа к обеим службам, используя один хост-адрес
  • Заключение и следующий шаг

Как разработчик Python, вы, возможно, слышали о терминах микросервисов, и хотите построить Micrevice Python самостоятельно. Микросервисы – отличная архитектура для создания высокомасштабируемых приложений. Перед началом создания приложения, используя микросервис, вы должны быть знакомы с преимуществами и недостатками использования микросервисов. В этой статье вы узнаете преимущества и недостатки использования микросервисов. Вы также узнаете, как вы можете создать свое собственное микросервис и развернуть его, используя Docker Compose.

В этом руководстве вы узнаете:

  • Какие преимущества и недостатки микросервисов
  • Почему вы должны построить микросервис с помощью Python
  • Как создать API отдыха с помощью Fastapi и PostgreSQL
  • Как создать микросервис, используя fastapi
  • Как запускать микросервисы, используя докер-состав
  • Как управлять микросервисами с помощью Nginx

Сначала вы постройте простой API для отдыха с помощью Fastapi, а затем используете PostgreSQL в качестве нашей базы данных. Затем вы продвигаете то же приложение для микровизации.

Введение в микросервисы

Micreadice – это подход от разрушения большого монолита применения в отдельных приложениях, специализирующихся на определенном обслуживании/функциональности. Этот подход часто известен как Ориентированная на обслуживание архитектура или SOA.

В Монолитная архитектура Каждая деловая логика проживает в том же приложении. Прикладные услуги, такие как управление пользователями, аутентификация и другие функции, используют одну и ту же базу данных.

В оформлении Архитектура микровисса Приложение разбито на несколько отдельных служб, которые работают в отдельных процессах. Существует разная база данных для различных функций приложения и службы взаимодействуют друг с другом с использованием HTTP, AMQP или двоичного протокола, такого как TCP, в зависимости от характера каждой службы. Межкладное сообщение также может быть выполнено с использованием очередей сообщений, таких как Кролик , Кафка или Redis Отказ

Преимущества микросервиса

Архитектура микросервиса поставляется с большим количеством преимуществ. Некоторые из этих преимуществ являются:

  • Свободно связанное приложение означает, что разные сервисы могут быть созданы с использованием технологий, которые им лучше всего. Таким образом, команда разработки не ограничена выбором, сделанным при запуске проекта.

  • Поскольку услуги несут ответственность за конкретные функции, которые облегчают понимание и сохранить приложение под контролем.

  • Масштабирование приложений также становится легче, потому что, если один из услуг требует высокого использования GPU, то только сервер, состоящий в том, чтобы сервис должен иметь высокий GPU, а другие могут работать на обычном сервере.

Недостатки микросервиса

Архитектура микросервиса не является серебряной пулью, которая решает все ваши проблемы, это тоже с его недостатками. Некоторые из этих недостатков:

  • Поскольку разные сервисы используют разные базы данных, которые требуются транзакции, связанные с более чем одной услугой, необходимо использовать возможную согласованность.

  • Идеальное расщепление услуг очень сложно достичь при первой попытке, и это необходимо итерацию, прежде чем прийти с наилучшим возможным разделением услуг.

  • Поскольку службы взаимодействуют друг с другом посредством использования сетевого взаимодействия, это делает приложение медленнее благодаря задержению сети и медленной службы.

Почему микросервис в Python

Python – идеальный инструмент для строительства микросервисов, потому что он поставляется с отличным сообществом, легкой кривой обучения и тонны библиотек. Благодаря введению асинхронного программирования в Python, Веб-каркасы С исполнением наравне с GO и Node.js появился.

Введение в Fastapi.

Fastapi – это современная высокопроизводительная, веб-каркас, которая поставляется с тоннами прохладных функций, таких как автоматическая документация на основе Openapi и встроенной библиотеки сериализации и валидации. Посмотреть здесь Для списка всех прохладных функций в Fastapi.

Почему fastapi.

Некоторые из причин, почему я думаю, что Fastapi – отличный выбор для строительства микросервисов в Python:

  • Автоматическая документация
  • Поддержка Async/ждать
  • Встроенная проверка и сериализация
  • 100% тип аннотирован, поэтому автозаполнение отлично работает

Установка fastapi.

Перед установкой fastapi создайте новый каталог movie_service и создать новую виртуальную среду внутри вновь созданного каталога, используя Виртуальский Отказ Если вы еще этого не установили Виртуальский :

pip install virtualenv

Теперь создайте новую виртуальную среду.

virtualenv env

Если вы находитесь на Mac/Linux, вы можете активировать виртуальную среду с помощью команды:

source ./env/bin/activate

Пользователи Windows могут запускать эту команду вместо этого:

.\env\Scripts\activate

Наконец, вы готовы установить Fastapi, запустите следующую команду:

pip install fastapi

Поскольку fastapi не приходит с встроенным сервисом, вам нужно установить Увикурн для этого бежать. Увикурн это Asgi Сервер, который позволяет нам использовать функции Async/a ждать. Установить Увикурн используя команду

pip install uvicorn

Создание простого API отдыха с помощью Fastapi

Прежде чем начать строить микросервис, используя fastapi, давайте узнаем основы fastapi. Создать новый каталог приложение и новый файл main.py Внутри недавно созданного каталога.

Добавьте следующий код в main.py Отказ

#~/movie_service/app/main.py

from fastapi import FastAPI

app = FastAPI()


@app.get('/')
async def index():
    return {"Real": "Python"}

Здесь вы сначала импортируете и создаете файл fastapi, а затем зарегистрируйте конечную точку root / Что затем возвращает JSON Отказ

Вы можете запустить сервер приложений, используя uvicorn app.main: App --reload Отказ Здесь app.main Указывает, что вы используете main.py Файл внутри приложение каталог и : приложение указывает на наше Fastapi Имя экземпляра.

Вы можете получить доступ к приложению от http://127.0.0.1:8000. . Чтобы получить доступ к прохладной автоматической документации, отправляйтесь на http://127.0.0.1:8000/docs. . Вы можете играть вокруг и взаимодействовать с вашим API из самого браузера.

Давайте добавим функцию Crud в наше приложение. Обновите свой main.py выглядеть следующее:

#~/movie_service/app/main.py

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

app = FastAPI()

fake_movie_db = [
    {
        'name': 'Star Wars: Episode IX - The Rise of Skywalker',
        'plot': 'The surviving members of the resistance face the First Order once again.',
        'genres': ['Action', 'Adventure', 'Fantasy'],
        'casts': ['Daisy Ridley', 'Adam Driver']
    }
]

class Movie(BaseModel):
    name: str
    plot: str
    genres: List[str]
    casts: List[str]


@app.get('/', response_model=List[Movie])
async def index():
    return fake_movie_db

Как вы можете видеть, вы создали новый класс Фильм который простирается Базомодель от пиданта. Фильм Модель содержит имя, фото, жанры и отливки. Pydantic Поставляется встроенный с fastapi, который делает создание моделей и запрос проверки ветер.

Если вы отправитесь на сайт Документа, вы можете увидеть, что есть поля модели нашей фильма, упомянутой уже в разделе «Ответ». Это возможно, потому что вы определили Ответ_model В нашем определении маршрута.

Теперь давайте добавим конечную точку, чтобы добавить фильм в наш список фильмов.

Добавьте новое определение конечной точки, чтобы обработать Пост запрос.

@app.post('/', status_code=201)
async def add_movie(payload: Movie):
    movie = payload.dict()
    fake_movie_db.append(movie)
    return {'id': len(fake_movie_db) - 1}

Теперь отправляйтесь в браузер и проверите новую API. Попробуйте добавить фильм с неверным полем или без необходимых полей и увидите, что проверка автоматически обрабатывается Fastapi.

Давайте добавим новую конечную точку для обновления фильма.

@app.put('/{id}')
async def update_movie(id: int, payload: Movie):
    movie = payload.dict()
    movies_length = len(fake_movie_db)
    if 0 <= id <= movies_length:
        fake_movie_db[id] = movie
        return None
    raise HTTPException(status_code=404, detail="Movie with given id not found")

Здесь ID Является ли индекс нашего fake_movie_db список.

Примечание: не забудьте импортировать HttpException. от fastapi.

Теперь вы также можете добавить конечную точку, чтобы удалить фильм.

@app.delete('/{id}')
async def delete_movie(id: int):
    movies_length = len(fake_movie_db)
    if 0 <= id <= movies_length:
        del fake_movie_db[id]
        return None
    raise HTTPException(status_code=404, detail="Movie with given id not found")

Прежде чем двигаться вперед, давайте построим наше приложение лучше. Создать новую папку API внутри приложение и создать новый файл Chames.py Внутри недавно созданной папки. Переместите все маршруты, связанные с «Коды» из main.py к Chames.py Отказ Итак, Chames.py должен выглядеть следующее:

#~/movie-service/app/api/movies.py

from typing import List
from fastapi import Header, APIRouter

from app.api.models import Movie

fake_movie_db = [
    {
        'name': 'Star Wars: Episode IX - The Rise of Skywalker',
        'plot': 'The surviving members of the resistance face the First Order once again.',
        'genres': ['Action', 'Adventure', 'Fantasy'],
        'casts': ['Daisy Ridley', 'Adam Driver']
    }
]

movies = APIRouter()

@movies.get('/', response_model=List[Movie])
async def index():
    return fake_movie_db

@movies.post('/', status_code=201)
async def add_movie(payload: Movie):
    movie = payload.dict()
    fake_movie_db.append(movie)
    return {'id': len(fake_movie_db) - 1}

@movies.put('/{id}')
async def update_movie(id: int, payload: Movie):
    movie = payload.dict()
    movies_length = len(fake_movie_db)
    if 0 <= id <= movies_length:
        fake_movie_db[id] = movie
        return None
    raise HTTPException(status_code=404, detail="Movie with given id not found")

@movies.delete('/{id}')
async def delete_movie(id: int):
    movies_length = len(fake_movie_db)
    if 0 <= id <= movies_length:
        del fake_movie_db[id]
        return None
    raise HTTPException(status_code=404, detail="Movie with given id not found")

Здесь вы зарегистрировали новый маршрут API, используя APIROUTER от Фастапи.

Кроме того, создайте новый файл Models.py внутри API Где вы будете держать наши пидантические модели.

#~/movie-service/api/models.py

from typing import List
from pydantic import BaseModel

class Movie(BaseModel):
    name: str
    plot: str
    genres: List[str]
    casts: List[str]

Теперь зарегистрируйте файл этих новых маршрутов в main.py.

#~/movie-service/app/main.py
from fastapi import FastAPI

from app.api.movies import movies

app = FastAPI()

app.include_router(movies)

Наконец, наша структура каталогов приложения выглядит так:

movie-service
├── app
│   ├── api
│   │   ├── models.py
│   │   ├── movies.py
│   |── main.py
└── env

Убедитесь, что ваше приложение работает должным образом, прежде чем двигаться вперед.

Использование PostgreSQL базы данных с fastapi

Ранее вы использовали поддельный список Python, чтобы добавить фильмы, но теперь вы, наконец, готовы использовать фактическую базу данных для этой цели. Вы собираетесь использовать PostgreSQL Для этого. Установить postgresql. Если вы еще не сделали. После установки PostgreSQL создайте новую базу данных, я собираюсь позвонить в мою работу movie_db Отказ

Вы собираетесь использовать Кодировать/базы данных Чтобы подключиться к базе данных, используя async. и ждать служба поддержки. Узнайте больше о Async/await в Python здесь

Установите необходимую библиотеку, используя:

pip install 'databases[postgresql]'

Это будет установить SQLalchemy и asyncpg Также, которые необходимы для работы с PostgreSQL.

Создайте новый файл внутри API и призвать это db.py Отказ Этот файл будет содержать фактическую модель базы данных для нашего API для отдыха.

#~/movie-service/app/api/db.py

from sqlalchemy import (Column, Integer, MetaData, String, Table,
                        create_engine, ARRAY)

from databases import Database

DATABASE_URL = 'postgresql://movie_user:movie_password@localhost/movie_db'

engine = create_engine(DATABASE_URL)
metadata = MetaData()

movies = Table(
    'movies',
    metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('plot', String(250)),
    Column('genres', ARRAY(String)),
    Column('casts', ARRAY(String))
)

database = Database(DATABASE_URL)

Здесь База данных_uri Является ли URL-адрес для подключения к базе данных PostgreSQL. Здесь movie_user Название пользователя базы данных movie_password это пароль пользователя базы данных и movie_db это название базы данных.

Так же, как вы бы в Sqlalchemy Вы создали таблицу для базы данных фильмов.

Обновление main.py подключиться к базе данных. main.py должен выглядеть следующее:

#~/movie-service/app/main.py

from fastapi import FastAPI
from app.api.movies import movies
from app.api.db import metadata, database, engine

metadata.create_all(engine)

app = FastAPI()

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()


app.include_router(movies)

Fastapi предоставляет некоторые обработчики событий, которые вы можете использовать для подключения к нашей базе данных, когда приложение запускается и отключается, когда он выключается.

Обновление Chames.py Так что он использует базу данных вместо поддельного списка Python.

#~/movie-service/app/api/movies.py


from typing import List
from fastapi import Header, APIRouter

from app.api.models import MovieIn, MovieOut
from app.api import db_manager

movies = APIRouter()

@movies.get('/', response_model=List[MovieOut])
async def index():
    return await db_manager.get_all_movies()

@movies.post('/', status_code=201)
async def add_movie(payload: MovieIn):
    movie_id = await db_manager.add_movie(payload)
    response = {
        'id': movie_id,
        **payload.dict()
    }

    return response

@movies.put('/{id}')
async def update_movie(id: int, payload: MovieIn):
    movie = payload.dict()
    fake_movie_db[id] = movie
    return None

@movies.put('/{id}')
async def update_movie(id: int, payload: MovieIn):
    movie = await db_manager.get_movie(id)
    if not movie:
        raise HTTPException(status_code=404, detail="Movie not found")

    update_data = payload.dict(exclude_unset=True)
    movie_in_db = MovieIn(**movie)

    updated_movie = movie_in_db.copy(update=update_data)

    return await db_manager.update_movie(id, updated_movie)

@movies.delete('/{id}')
async def delete_movie(id: int):
    movie = await db_manager.get_movie(id)
    if not movie:
        raise HTTPException(status_code=404, detail="Movie not found")
    return await db_manager.delete_movie(id)

Давайте добавим db_manager.py манипулировать нашей базой данных.

#~/movie-service/app/api/db_manager.py

from app.api.models import MovieIn, MovieOut, MovieUpdate
from app.api.db import movies, database


async def add_movie(payload: MovieIn):
    query = movies.insert().values(**payload.dict())

    return await database.execute(query=query)

async def get_all_movies():
    query = movies.select()
    return await database.fetch_all(query=query)

async def get_movie(id):
    query = movies.select(movies.c.id==id)
    return await database.fetch_one(query=query)

async def delete_movie(id: int):
    query = movies.delete().where(movies.c.id==id)
    return await database.execute(query=query)

async def update_movie(id: int, payload: MovieIn):
    query = (
        movies
        .update()
        .where(movies.c.id == id)
        .values(**payload.dict())
    )
    return await database.execute(query=query)

Давайте обновим наш Models.py Так что вы можете использовать пидантическую модель с таблицей SQLALCHEMY.

#~/movie-service/app/api/models.py

from pydantic import BaseModel
from typing import List, Optional

class MovieIn(BaseModel):
    name: str
    plot: str
    genres: List[str]
    casts: List[str]


class MovieOut(MovieIn):
    id: int


class MovieUpdate(MovieIn):
    name: Optional[str] = None
    plot: Optional[str] = None
    genres: Optional[List[str]] = None
    casts: Optional[List[str]] = None

Здесь Сейн Является ли базовая модель, которую вы используете для добавления фильма в базу данных. Вы должны добавить ID С этой модели при получении этого из базы данных, следовательно, MoveOut модель. MovieUpdate Модель позволяет нам устанавливать значения в модели, чтобы быть необязательной, чтобы при обновлении фильма только поле, которое необходимо обновить.

Теперь отправляйтесь на сайт документации браузера и начните играть с API.

Шаблоны управления данными Micreervice

Управление данными в микросервизе является одним из самых сложных аспектов построения микросервиса. Поскольку различные функции приложения обрабатываются различными службами, использование базы данных может быть сложно.

Вот несколько шаблонов, которые вы можете использовать для управления потоком данных в приложении.

База данных за услугу

Использование базы данных за услугу отлично, если вы хотите, чтобы ваши микросервисы были как можно более слабо сочетаться. Наличие другой базы данных на сервис позволяет нам масштабировать различные услуги самостоятельно. Транзакция, связанная с несколькими базами данных, выполняется через четко определенные API. Это поставляется с его недостатком, так как реализация бизнес-транзакций, связанных с несколькими услугами, не просты. Кроме того, добавление накладных расходов сети делает это менее эффективным для использования.

Общая база данных

Если есть много транзакций, связанных с несколькими услугами, лучше использовать общую базу данных. Это поставляется с преимуществами весьма последовательного применения, но убирает большинство преимуществ, которые поставляются с архитектурой микросервисов. Разработчики, работающие над одним сервисом, должны координировать с изменениями схемы в других услугах.

Композиция API.

В сделках, связанных с несколькими базами данных, API Composer действует как шлюз API, и выполняет вызовы API в другие микросервисы в требуемом порядке. Наконец, результаты каждых микросервисов возвращаются на службу клиента после выполнения соединения в памяти. Нижняя часть этого подхода неэффективна в памяти в память о большом наборе данных.

Создание Python Micreervice в Docker

Боль в развертывании микросервиса может быть значительно уменьшена с помощью Docker. Docker помогает инкапсулировать каждую услугу и масштабировать их независимо.

Установка Docker и Docker Compose

Если вы еще не сделали Установить Docker в вашей системе. Проверьте, установлен ли Docker, запустив команду Докер Отказ После того, как вы сделали установку Docker, Установить Docker Compose Отказ Docker Compose используется для определения и запуска нескольких контейнеров докеров. Это также помогает в легком взаимодействии между ними.

Создание фильмов Service.

Поскольку многие работы для создания сервиса фильма уже сделаны при работе с Fastapi, вы собираетесь повторно использовать код, который вы уже написали. Создайте совершенно новую папку, я собираюсь позвонить мне Python-microservices Отказ Переместите код, который вы написали ранее, которое я назвал Movie-Service Отказ Итак, структура папки будет выглядеть так:

python-microservices/
└── movie-service/
    ├── app/
    └── env/

Прежде всего, давайте создадим требования .txt Файл, где вы собираетесь сохранить все зависимости, которые вы собираетесь использовать в нашем кино-сервис . Создайте новый файл требования .txt внутри Movie-Service и добавьте следующее к нему:

asyncpg==0.20.1
databases[postgresql]==0.2.6
fastapi==0.48.0
SQLAlchemy==1.3.13
uvicorn==0.11.2
httpx==0.11.1

Вы использовали все библиотеки, упомянутые там, кроме httpx Что вы собираетесь использовать при создании сервиса для обслуживания API-вызова.

Создать Dockerfile внутри Movie-Service со следующим содержанием:

FROM python:3.8-slim

WORKDIR /app

COPY ./requirements.txt /app/requirements.txt

RUN apt-get update \
    && apt-get install gcc -y \
    && apt-get clean

RUN pip install -r /app/requirements.txt \
    && rm -rf /root/.cache/pip

COPY . /app/

Во-первых, вы определяете, какую версию Python вы хотите использовать. Затем установите Workdir быть внутри приложение Папка внутри контейнера докера. После этого GCC Установлен, который требуется библиотеками, которые вы используете в приложении. Наконец, установите все зависимости в требования .txt и скопируйте все файлы внутри Movie-Service/App .

Обновление db.py и заменить

DATABASE_URI = 'postgresql://movie_user:movie_password@localhost/movie_db'

с участием

DATABASE_URI = os.getenv('DATABASE_URI')

Примечание: не забудьте импортировать Операционные системы на вершине файла.

Вам нужно сделать это так, чтобы вы могли проводить База данных_uri как переменная среды.

Кроме того, обновите main.py и заменить

app.include_router(movies)

с участием

app.include_router(movies, prefix='/api/v1/movies', tags=['movies'])

Здесь вы добавили префикс /API/V1/фильмы Итак, что управление разной версией API становится легче. Кроме того, теги делают поиск API, связанные с Фильмы Проще в документах Fastapi.

Кроме того, вам нужно обновить наши модели, чтобы Chasts хранит идентификатор кафедры вместо фактического имени. Итак, обновите Models.py выглядеть так:

#~/python-microservices/movie-service/app/api/models.py

from pydantic import BaseModel
from typing import List, Optional

class MovieIn(BaseModel):
    name: str
    plot: str
    genres: List[str]
    casts_id: List[int]


class MovieOut(MovieIn):
    id: int


class MovieUpdate(MovieIn):
    name: Optional[str] = None
    plot: Optional[str] = None
    genres: Optional[List[str]] = None
    casts_id: Optional[List[int]] = None

Аналогично, вам нужно обновить таблицы базы данных, давайте обновимся db.py :

#~/python-microservices/movie-service/app/api/db.py

import os

from sqlalchemy import (Column, DateTime, Integer, MetaData, String, Table,
                        create_engine, ARRAY)

from databases import Database

DATABASE_URL = os.getenv('DATABASE_URL')

engine = create_engine(DATABASE_URL)
metadata = MetaData()

movies = Table(
    'movies',
    metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('plot', String(250)),
    Column('genres', ARRAY(String)),
    Column('casts_id', ARRAY(Integer))
)

database = Database(DATABASE_URL)

Теперь обновите Chames.py Чтобы проверить, есть ли актеры с указанным идентификатором в литой услуге перед добавлением нового фильма или обновления фильма.

#~/python-microservices/movie-service/app/api/movies.py

from typing import List
from fastapi import APIRouter, HTTPException

from app.api.models import MovieOut, MovieIn, MovieUpdate
from app.api import db_manager
from app.api.service import is_cast_present

movies = APIRouter()

@movies.post('/', response_model=MovieOut, status_code=201)
async def create_movie(payload: MovieIn):
    for cast_id in payload.casts_id:
        if not is_cast_present(cast_id):
            raise HTTPException(status_code=404, detail=f"Cast with id:{cast_id} not found")

    movie_id = await db_manager.add_movie(payload)
    response = {
        'id': movie_id,
        **payload.dict()
    }

    return response

@movies.get('/', response_model=List[MovieOut])
async def get_movies():
    return await db_manager.get_all_movies()

@movies.get('/{id}/', response_model=MovieOut)
async def get_movie(id: int):
    movie = await db_manager.get_movie(id)
    if not movie:
        raise HTTPException(status_code=404, detail="Movie not found")
    return movie

@movies.put('/{id}/', response_model=MovieOut)
async def update_movie(id: int, payload: MovieUpdate):
    movie = await db_manager.get_movie(id)
    if not movie:
        raise HTTPException(status_code=404, detail="Movie not found")

    update_data = payload.dict(exclude_unset=True)

    if 'casts_id' in update_data:
        for cast_id in payload.casts_id:
            if not is_cast_present(cast_id):
                raise HTTPException(status_code=404, detail=f"Cast with given id:{cast_id} not found")

    movie_in_db = MovieIn(**movie)

    updated_movie = movie_in_db.copy(update=update_data)

    return await db_manager.update_movie(id, updated_movie)

@movies.delete('/{id}', response_model=None)
async def delete_movie(id: int):
    movie = await db_manager.get_movie(id)
    if not movie:
        raise HTTPException(status_code=404, detail="Movie not found")
    return await db_manager.delete_movie(id)

Давайте добавим услугу, чтобы сделать вызов API для CAST SERVICE:

#~/python-microservices/movie-service/app/api/service.py

import os
import httpx

CAST_SERVICE_HOST_URL = 'http://localhost:8002/api/v1/casts/'
url = os.environ.get('CAST_SERVICE_HOST_URL') or CAST_SERVICE_HOST_URL

def is_cast_present(cast_id: int):
    r = httpx.get(f'{url}{cast_id}')
    return True if r.status_code == 200 else False

Вы делаете вызов API, чтобы получить отливку с данным удостоверение личности и вернуть True, если аккумулятор существует и ложь иначе.

Создание Chasts Service

Похож на Movie-Service , для создания Casts-Service Вы собираетесь использовать базу данных Fastapi и PostgreSQL.

Создайте структуру папки, как следующее:

python-microservices/
.
├── cast_service/
│   ├── app/
│   │   ├── api/
│   │   │   ├── casts.py
│   │   │   ├── db_manager.py
│   │   │   ├── db.py
│   │   │   ├── models.py
│   │   ├── main.py
│   ├── Dockerfile
│   └── requirements.txt
├── movie_service/
...

Добавьте следующее в требования .txt :

asyncpg==0.20.1
databases[postgresql]==0.2.6
fastapi==0.48.0
SQLAlchemy==1.3.13
uvicorn==0.11.2

Dockerfile :

FROM python:3.8-slim

WORKDIR /app

COPY ./requirements.txt /app/requirements.txt

RUN apt-get update \
    && apt-get install gcc -y \
    && apt-get clean

RUN pip install -r /app/requirements.txt \
    && rm -rf /root/.cache/pip

COPY . /app/

main.py

#~/python-microservices/cast-service/app/main.py

from fastapi import FastAPI
from app.api.casts import casts
from app.api.db import metadata, database, engine

metadata.create_all(engine)

app = FastAPI()

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()

app.include_router(casts, prefix='/api/v1/casts', tags=['casts'])

Вы добавили префикс /API/V1/Chasts Так что управление API становится легче. Также, добавляя Теги делает поиск документов, связанных с Chasts в документах Fastapi проще.

casts.py

#~/python-microservices/cast-service/app/api/casts.py

from fastapi import APIRouter, HTTPException
from typing import List

from app.api.models import CastOut, CastIn, CastUpdate
from app.api import db_manager

casts = APIRouter()

@casts.post('/', response_model=CastOut, status_code=201)
async def create_cast(payload: CastIn):
    cast_id = await db_manager.add_cast(payload)

    response = {
        'id': cast_id,
        **payload.dict()
    }

    return response

@casts.get('/{id}/', response_model=CastOut)
async def get_cast(id: int):
    cast = await db_manager.get_cast(id)
    if not cast:
        raise HTTPException(status_code=404, detail="Cast not found")
    return cast

db_manager.py

#~/python-microservices/cast-service/app/api/db_manager.py

from app.api.models import CastIn, CastOut, CastUpdate
from app.api.db import casts, database


async def add_cast(payload: CastIn):
    query = casts.insert().values(**payload.dict())

    return await database.execute(query=query)

async def get_cast(id):
    query = casts.select(casts.c.id==id)
    return await database.fetch_one(query=query)

db.py

#~/python-microservices/cast-service/app/api/db.py

import os

from sqlalchemy import (Column, Integer, MetaData, String, Table,
                        create_engine, ARRAY)

from databases import Database

DATABASE_URI = os.getenv('DATABASE_URI')

engine = create_engine(DATABASE_URI)
metadata = MetaData()

casts = Table(
    'casts',
    metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('nationality', String(20)),
)

database = Database(DATABASE_URI)

models.py

#~/python-microservices/cast-service/app/api/models.py

from pydantic import BaseModel
from typing import List, Optional

class CastIn(BaseModel):
    name: str
    nationality: Optional[str] = None


class CastOut(CastIn):
    id: int


class CastUpdate(CastIn):
    name: Optional[str] = None

Запуск микросервиса с помощью Docker Compose

Чтобы запустить микросервисы, создайте Docker-Compose.yml Файл и добавьте следующее к нему:

version: '3.7'

services:
  movie_service:
    build: ./movie-service
    command: uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
    volumes:
      - ./movie-service/:/app/
    ports:
      - 8001:8000
    environment:
      - DATABASE_URI=postgresql://movie_db_username:movie_db_password@movie_db/movie_db_dev
      - CAST_SERVICE_HOST_URL=http://cast_service:8000/api/v1/casts/

  movie_db:
    image: postgres:12.1-alpine
    volumes:
      - postgres_data_movie:/var/lib/postgresql/data/
    environment:
      - POSTGRES_USER=movie_db_username
      - POSTGRES_PASSWORD=movie_db_password
      - POSTGRES_DB=movie_db_dev

  cast_service:
    build: ./cast-service
    command: uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
    volumes:
      - ./cast-service/:/app/
    ports:
      - 8002:8000
    environment:
      - DATABASE_URI=postgresql://cast_db_username:cast_db_password@cast_db/cast_db_dev

  cast_db:
    image: postgres:12.1-alpine
    volumes:
      - postgres_data_cast:/var/lib/postgresql/data/
    environment:
      - POSTGRES_USER=cast_db_username
      - POSTGRES_PASSWORD=cast_db_password
      - POSTGRES_DB=cast_db_dev

volumes:
  postgres_data_movie:
  postgres_data_cast:

Здесь у вас есть 4 разных сервиса, Movie_Service, база данных для Movie_Service, Cast_Service и базы данных для CAST SERVICE. Вы открыли movie_service портировать 8001 Точно так же Cast_Service портировать 8002 Отказ

Для базы данных вы использовали тома, чтобы данные не были разрушены, когда контейнер Docker выключен.

Запустите Docker-Compose, используя команду:

docker-compose up -d

Это создает изображение докера, если он еще не существует и запускает их.

Перейти к http://localhost: 8002/Документы добавить актеры в Casts Service. Точно так же http://localhost: 8001/Документы Чтобы добавить фильм в фильме услуги.

Использование Nginx для доступа к обеим службам, используя один хост-адрес

Вы развернули микросервисы, используя Docker Compose, но есть одна незначительная проблема. Каждый из микросервисов должен быть доступен с использованием другого порта. Вы можете решить эту проблему, используя Nginx обратный прокси Используя Nginx, вы можете направить запрос, добавить промежуточное программное обеспечение, которое направляет наши запросы на разные услуги на основе URL API.

Добавить новый файл nginx_config.conf. внутри Python-microservices со следующим содержанием.

server {
  listen 8080;

  location /api/v1/movies {
    proxy_pass http://movie_service:8000/api/v1/movies;
  }

  location /api/v1/casts {
    proxy_pass http://cast_service:8000/api/v1/casts;
  }

}

Здесь вы управляете Nginx в порту 8080 и маршрутизация запросов к сервису фильма, если конечная точка запускается с /API/V1/фильмы и аналогично лишать услуги, если конечная точка начинается с /API/V1/Chasts

Теперь вам нужно добавить сервис Nginx в нашем Docker-Compose-YML Отказ Добавьте следующую услугу после cast_db услуга:

nginx:
    image: nginx:latest
    ports:
      - "8080:8080"
    volumes:
      - ./nginx_config.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - cast_service
      - movie_service

Теперь выключите контейнеры командой:

docker-compose down

И снова запустите его с:

docker-compose up -d

Теперь вы можете получить доступ как на сервисе фильма, так и в HAST SERVICE в Port 8080 Отказ Перейти к http://localhost: 8080/API/V1/фильмы/ Чтобы получить список фильмов.

Теперь вам может быть интересно, как вы можете получить доступ к документам услуг. Для этого обновления main.py киносервиса и заменять

app = FastAPI()

с участием

app = FastAPI(openapi_url="/api/v1/movies/openapi.json", docs_url="/api/v1/movies/docs")

Аналогично, для литой услуги замените его

app = FastAPI(openapi_url="/api/v1/casts/openapi.json", docs_url="/api/v1/casts/docs")

Здесь вы изменились, какая конечная точка подается документы и откуда openapi.json подается.

Теперь вы можете получить доступ к документам из http://localhost: 8080/API/V1/фильмы/Документы Для кино сервиса и от http://localhost: 8080/API/V1/Chasts/Docs для Casts Service.

Если вы застряли в какой-то момент или просто хотите посмотреть на полный код, отправляйтесь на Github repo.

Заключение и следующий шаг

Архитектура микросервиса отлично подходит для разрушения большого монолитного применения в отдельной бизнес-логике, но это тоже приходит с осложнением. Python отлично подходит для строительства микросервиса из-за опыта разработчика и тонн пакетов и структур, чтобы сделать разработчики более продуктивными.

Развертывание микросервисов стало легче благодаря Docker. Узнайте больше на Как разрабатывать микросервисы, используя Docker, а также Docker Compose

Хотите, чтобы я охватил любую тему? Дайте мне знать в твиттер или напишите комментарий ниже.

Оригинал: “https://dev.to/paurakhsharma/microservice-in-python-using-fastapi-24cc”