- Введение в микросервисы
- Преимущества микросервиса
- Недостатки микросервиса
- Почему микросервис в 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”