putilin_pavel_lab_3
This commit is contained in:
parent
9808787f27
commit
3ef7c65962
66
putilin_pavel_lab_3/README.md
Normal file
66
putilin_pavel_lab_3/README.md
Normal file
@ -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
|
||||||
|
|
10
putilin_pavel_lab_3/client_service/Dockerfile
Normal file
10
putilin_pavel_lab_3/client_service/Dockerfile
Normal file
@ -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"]
|
39
putilin_pavel_lab_3/client_service/app.py
Normal file
39
putilin_pavel_lab_3/client_service/app.py
Normal file
@ -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"}
|
2
putilin_pavel_lab_3/client_service/requirements.txt
Normal file
2
putilin_pavel_lab_3/client_service/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
fastapi
|
||||||
|
uvicorn
|
36
putilin_pavel_lab_3/docker-compose.yml
Normal file
36
putilin_pavel_lab_3/docker-compose.yml
Normal file
@ -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
|
BIN
putilin_pavel_lab_3/image_1.png
Normal file
BIN
putilin_pavel_lab_3/image_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
putilin_pavel_lab_3/image_2.png
Normal file
BIN
putilin_pavel_lab_3/image_2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
5
putilin_pavel_lab_3/nginx/Dockerfile
Normal file
5
putilin_pavel_lab_3/nginx/Dockerfile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
17
putilin_pavel_lab_3/nginx/nginx.conf
Normal file
17
putilin_pavel_lab_3/nginx/nginx.conf
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
10
putilin_pavel_lab_3/order_service/Dockerfile
Normal file
10
putilin_pavel_lab_3/order_service/Dockerfile
Normal file
@ -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"]
|
72
putilin_pavel_lab_3/order_service/app.py
Normal file
72
putilin_pavel_lab_3/order_service/app.py
Normal file
@ -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"}
|
3
putilin_pavel_lab_3/order_service/requirements.txt
Normal file
3
putilin_pavel_lab_3/order_service/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fastapi
|
||||||
|
uvicorn
|
||||||
|
requests
|
Loading…
Reference in New Issue
Block a user