diff --git a/putilin_pavel_lab_3/README.md b/putilin_pavel_lab_3/README.md new file mode 100644 index 0000000..77bc0f0 --- /dev/null +++ b/putilin_pavel_lab_3/README.md @@ -0,0 +1,66 @@ +# Лабораторная работа №3 - REST API, шлюз и синхронный обмен данными между микросервисами + +## Задание + +### Цель: +Изучение принципов проектирования с использованием паттерна шлюза, организации синхронной передачи данных между микросервисами и применения архитектурного стиля RESTful API. + +### Задачи: +1. Создание двух микросервисов, которые реализуют операции CRUD для связанных сущностей (клиенты и заказы). +2. Реализация механизма синхронного обмена данными между микросервисами. +3. Настройка шлюза на базе Nginx в качестве прозрачного прокси-сервера. + +### Микросервисы: +1. **client_service** — сервис, который управляет информацией о клиентах. +2. **order_service** — сервис, который обрабатывает данные о заказах, сделанных клиентами. + +### Связь между микросервисами: +- Один клиент может иметь множество заказов (соотношение 1 ко многим). + +### Как запустить проект: +Для запуска приложения необходимо выполнить команду: +``` +docker-compose up +``` + +## Описание работы: + +Для разработки микросервисов был выбран язык программирования Python с использованием фреймворка Flask. Каждый микросервис реализует стандартные операции CRUD (создание, чтение, обновление и удаление) для своей сущности. + +### Синхронный обмен данными +Микросервис `order_service` отправляет HTTP-запросы к сервису `client_service` для получения актуальной информации о клиентах, с которыми связаны заказы. Это позволяет создавать заказ с привязкой к конкретному клиенту, а также обновлять или удалять данные о заказах, учитывая информацию о клиенте. + +### Docker Compose +Конфигурационный файл `docker-compose.yml` представляет собой многоконтейнерное приложение, которое включает в себя три сервиса: `client_service`, `order_service` и `nginx`. С помощью Docker Compose можно запустить все три контейнера одновременно. Функция маршрутизации запросов возложена на сервер Nginx, который управляет входящими запросами и перенаправляет их на соответствующие микросервисы. + +### Nginx +Конфигурационный файл Nginx (`nginx.conf`) управляет маршрутизацией входящих запросов. В зависимости от URL, запросы перенаправляются на сервисы: + +- Запросы по пути `/client/` перенаправляются на сервис `client_service`. +- Запросы по пути `/order/` перенаправляются на сервис `order_service`. + +Пример конфигурации Nginx: +```nginx +server { + listen 80; + + location /client/ { + proxy_pass http://client_service:8000; + } + + location /order/ { + proxy_pass http://order_service:8001; + } +} +``` + +### Взаимодействие между микросервисами +- **client_service**: Этот сервис управляет информацией о клиентах (например, имя клиента, номер и дата выдачи). +- **order_service**: Этот сервис управляет заказами и хранит информацию о каждом заказе, связанном с конкретным клиентом. Заказы могут быть созданы, обновлены или удалены через RESTful API. + +Когда заказ создается или обновляется в `order_service`, он отправляет запрос к `client_service`, чтобы получить подробную информацию о клиенте (например, имя, номер и дата выдачи абонемента). + +### ВИДЕО + +https://cloud.mail.ru/public/oG1Y/GXywRodDN + diff --git a/putilin_pavel_lab_3/client_service/Dockerfile b/putilin_pavel_lab_3/client_service/Dockerfile new file mode 100644 index 0000000..4fd96e9 --- /dev/null +++ b/putilin_pavel_lab_3/client_service/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.10-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/putilin_pavel_lab_3/client_service/app.py b/putilin_pavel_lab_3/client_service/app.py new file mode 100644 index 0000000..5c81452 --- /dev/null +++ b/putilin_pavel_lab_3/client_service/app.py @@ -0,0 +1,39 @@ +from fastapi import FastAPI, HTTPException +from uuid import uuid4 + +app = FastAPI() + +# Временное хранилище для клиентов +clients = {} + +@app.get("/") +def get_clients(): + return list(clients.values()) + +@app.get("/{client_id}") +def get_client(client_id: str): + if client_id not in clients: + raise HTTPException(status_code=404, detail="Client not found") + return clients[client_id] + +@app.post("/") +def create_client(client: dict): + client_id = str(uuid4()) + new_client = {"id": client_id, **client} + clients[client_id] = new_client + return new_client + +@app.put("/{client_id}") +def update_client(client_id: str, client: dict): + if client_id not in clients: + raise HTTPException(status_code=404, detail="Client not found") + updated_client = {"id": client_id, **client} + clients[client_id] = updated_client + return updated_client + +@app.delete("/{client_id}") +def delete_client(client_id: str): + if client_id not in clients: + raise HTTPException(status_code=404, detail="Client not found") + del clients[client_id] + return {"detail": "Client deleted successfully"} diff --git a/putilin_pavel_lab_3/client_service/requirements.txt b/putilin_pavel_lab_3/client_service/requirements.txt new file mode 100644 index 0000000..97dc7cd --- /dev/null +++ b/putilin_pavel_lab_3/client_service/requirements.txt @@ -0,0 +1,2 @@ +fastapi +uvicorn diff --git a/putilin_pavel_lab_3/docker-compose.yml b/putilin_pavel_lab_3/docker-compose.yml new file mode 100644 index 0000000..388fedd --- /dev/null +++ b/putilin_pavel_lab_3/docker-compose.yml @@ -0,0 +1,36 @@ +version: "3.8" + +services: + client_service: + build: + context: ./client_service + container_name: client_service + ports: + - "8000:8000" + networks: + - app_network + + order_service: + build: + context: ./order_service + container_name: order_service + ports: + - "8001:8001" + networks: + - app_network + environment: + - CLIENT_SERVICE_URL=http://client_service:8000 + + nginx: + image: nginx:alpine + container_name: nginx + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + networks: + - app_network + +networks: + app_network: + driver: bridge diff --git a/putilin_pavel_lab_3/image_1.png b/putilin_pavel_lab_3/image_1.png new file mode 100644 index 0000000..f8712e2 Binary files /dev/null and b/putilin_pavel_lab_3/image_1.png differ diff --git a/putilin_pavel_lab_3/image_2.png b/putilin_pavel_lab_3/image_2.png new file mode 100644 index 0000000..0e39dec Binary files /dev/null and b/putilin_pavel_lab_3/image_2.png differ diff --git a/putilin_pavel_lab_3/nginx/Dockerfile b/putilin_pavel_lab_3/nginx/Dockerfile new file mode 100644 index 0000000..ff0efa0 --- /dev/null +++ b/putilin_pavel_lab_3/nginx/Dockerfile @@ -0,0 +1,5 @@ +FROM nginx:alpine + +COPY nginx.conf /etc/nginx/nginx.conf + +EXPOSE 80 diff --git a/putilin_pavel_lab_3/nginx/nginx.conf b/putilin_pavel_lab_3/nginx/nginx.conf new file mode 100644 index 0000000..3fb4a37 --- /dev/null +++ b/putilin_pavel_lab_3/nginx/nginx.conf @@ -0,0 +1,17 @@ +server { + listen 80; + + location /client/ { + proxy_pass http://client_service:8000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /order/ { + proxy_pass http://order_service:8001/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} diff --git a/putilin_pavel_lab_3/order_service/Dockerfile b/putilin_pavel_lab_3/order_service/Dockerfile new file mode 100644 index 0000000..7c3e688 --- /dev/null +++ b/putilin_pavel_lab_3/order_service/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.10-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8001"] diff --git a/putilin_pavel_lab_3/order_service/app.py b/putilin_pavel_lab_3/order_service/app.py new file mode 100644 index 0000000..8505d6c --- /dev/null +++ b/putilin_pavel_lab_3/order_service/app.py @@ -0,0 +1,72 @@ +from fastapi import FastAPI, HTTPException +from uuid import uuid4 +import requests + +app = FastAPI() + +# Временное хранилище для заказов +orders = {} + +# URL микросервиса клиентов (обновим при подключении Docker Compose) +CLIENT_SERVICE_URL = "http://client_service:8000" + + +@app.get("/") +def get_orders(): + return list(orders.values()) + + +@app.get("/{order_id}") +def get_order(order_id: str): + if order_id not in orders: + raise HTTPException(status_code=404, detail="Order not found") + return orders[order_id] + + +@app.post("/") +def create_order(order: dict): + # Проверка существования клиента + client_id = order.get("client_id") + if not client_id: + raise HTTPException(status_code=400, detail="client_id is required") + + try: + response = requests.get(f"{CLIENT_SERVICE_URL}/{client_id}") + response.raise_for_status() + except requests.exceptions.RequestException: + raise HTTPException(status_code=404, detail="Client not found") + + # Создание заказа + order_id = str(uuid4()) + new_order = {"id": order_id, **order} + orders[order_id] = new_order + return new_order + + +@app.put("/{order_id}") +def update_order(order_id: str, order: dict): + if order_id not in orders: + raise HTTPException(status_code=404, detail="Order not found") + + # Проверка существования клиента + client_id = order.get("client_id") + if not client_id: + raise HTTPException(status_code=400, detail="client_id is required") + + try: + response = requests.get(f"{CLIENT_SERVICE_URL}/{client_id}") + response.raise_for_status() + except requests.exceptions.RequestException: + raise HTTPException(status_code=404, detail="Client not found") + + updated_order = {"id": order_id, **order} + orders[order_id] = updated_order + return updated_order + + +@app.delete("/{order_id}") +def delete_order(order_id: str): + if order_id not in orders: + raise HTTPException(status_code=404, detail="Order not found") + del orders[order_id] + return {"detail": "Order deleted successfully"} diff --git a/putilin_pavel_lab_3/order_service/requirements.txt b/putilin_pavel_lab_3/order_service/requirements.txt new file mode 100644 index 0000000..5e7e855 --- /dev/null +++ b/putilin_pavel_lab_3/order_service/requirements.txt @@ -0,0 +1,3 @@ +fastapi +uvicorn +requests