kosheev_maksim_lab_3 is ready #230
9
kosheev_maksim_lab_3/Dockerfile
Normal file
9
kosheev_maksim_lab_3/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM python:3.9
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask requests
|
||||
|
||||
CMD ["python", "subscriptions_service.py"] # Замените на нужный файл при сборке.
|
46
kosheev_maksim_lab_3/Readmy.md
Normal file
46
kosheev_maksim_lab_3/Readmy.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Лабораторная работа №3 - REST API, шлюз и синхронный обмен данными между микросервисами
|
||||
|
||||
## Задание
|
||||
|
||||
### Цель:
|
||||
Изучение принципов проектирования с использованием паттерна шлюза, организации синхронной передачи данных между микросервисами и применения архитектурного стиля RESTful API.
|
||||
|
||||
### Задачи:
|
||||
1. Создание двух микросервисов, которые реализуют операции CRUD для связанных сущностей.
|
||||
2. Реализация механизма синхронного обмена данными между микросервисами.
|
||||
3. Настройка шлюза на базе Nginx в качестве прозрачного прокси-сервера.
|
||||
|
||||
### Микросервисы:
|
||||
1. **books_service** — сервис, который управляет информацией о книгах.
|
||||
2. **subscriptions_service** — сервис, который обрабатывает данные об абонементах.
|
||||
|
||||
### Связь между микросервисами:
|
||||
- Один абонемент может быть связан с несколькими книгами. Взаимодействие между сервисами осуществляется через синхронные HTTP-запросы.
|
||||
|
||||
## Как запустить проект:
|
||||
|
||||
Для запуска приложения необходимо выполнить команду:
|
||||
|
||||
```bash
|
||||
docker-compose up --build
|
||||
```
|
||||
После этого проект будет доступен на следующих адресах:
|
||||
|
||||
Books Service: http://localhost:5000/books/
|
||||
Subscriptions Service: http://localhost:5001/subscriptions/
|
||||
## Описание работы:
|
||||
Для разработки микросервисов был выбран язык программирования Python, с использованием фреймворка Flask для реализации API.
|
||||
|
||||
## Синхронный обмен данными
|
||||
Сервис books_service обращается к subscriptions_service для получения информации о подписках, связанных с каждой книгой.
|
||||
Все запросы между сервисами выполняются синхронно с использованием HTTP-запросов, что позволяет обеспечить актуальность данных при выполнении операций CRUD.
|
||||
Docker Compose
|
||||
Конфигурационный файл docker-compose.yml представляет собой многоконтейнерное приложение, которое включает в себя три сервиса: books_service, subscriptions_service и nginx. С помощью Docker Compose можно удобно запустить все сервисы одновременно.
|
||||
|
||||
## Nginx
|
||||
Конфигурация Nginx настроена так, чтобы обрабатывать входящие HTTP-запросы и перенаправлять их на соответствующие сервисы. Это позволяет использовать один общий адрес для доступа к разным API-сервисам.
|
||||
|
||||
Nginx будет действовать как шлюз, который будет маршрутизировать запросы от пользователей в соответствующие микросервисы.
|
||||
|
||||
### Видео
|
||||
https://disk.yandex.ru/i/b12BECyaGmbqUQ
|
8
kosheev_maksim_lab_3/books_service/Dockerfile
Normal file
8
kosheev_maksim_lab_3/books_service/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
COPY requirements.txt requirements.txt
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY . .
|
||||
|
||||
CMD ["python", "app.py"]
|
82
kosheev_maksim_lab_3/books_service/app.py
Normal file
82
kosheev_maksim_lab_3/books_service/app.py
Normal file
@ -0,0 +1,82 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import requests
|
||||
import uuid
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Данные о книгах (в памяти)
|
||||
books = [
|
||||
{
|
||||
"uuid": str(uuid.uuid4()),
|
||||
"author": "J.K.R.",
|
||||
"subject": "HP and PS",
|
||||
"year": 1997,
|
||||
"subscriptionUuid": "8f036445-a5bd-401c-926e-840f9de795cd"
|
||||
}
|
||||
]
|
||||
|
||||
SUBSCRIPTIONS_URL = "http://subscriptions_service:5000/subscriptions/"
|
||||
|
||||
|
||||
@app.route('/books/', methods=['GET'])
|
||||
def get_books():
|
||||
return jsonify(books), 200
|
||||
|
||||
|
||||
@app.route('/books/<uuid:book_id>', methods=['GET'])
|
||||
def get_book(book_id):
|
||||
book = next((b for b in books if b['uuid'] == str(book_id)), None)
|
||||
if not book:
|
||||
return jsonify({"error": "Book not found"}), 404
|
||||
|
||||
# Получение данных об абонементе
|
||||
response = requests.get(f"{SUBSCRIPTIONS_URL}{book['subscriptionUuid']}")
|
||||
if response.status_code == 200:
|
||||
book["subscriptionInfo"] = response.json()
|
||||
return jsonify(book), 200
|
||||
|
||||
|
||||
@app.route('/books/', methods=['POST'])
|
||||
def create_book():
|
||||
data = request.json
|
||||
new_book = {
|
||||
"uuid": str(uuid.uuid4()),
|
||||
"author": data["author"],
|
||||
"subject": data["subject"],
|
||||
"year": data["year"],
|
||||
"subscriptionUuid": data["subscriptionUuid"]
|
||||
}
|
||||
books.append(new_book)
|
||||
return jsonify(new_book), 201
|
||||
|
||||
|
||||
@app.route('/books/<uuid:book_id>', methods=['PUT'])
|
||||
def update_book(book_id):
|
||||
data = request.json
|
||||
book = next((b for b in books if b['uuid'] == str(book_id)), None)
|
||||
if not book:
|
||||
return jsonify({"error": "Book not found"}), 404
|
||||
|
||||
# Обновляем данные книги
|
||||
book["author"] = data.get("author", book["author"])
|
||||
book["subject"] = data.get("subject", book["subject"])
|
||||
book["year"] = data.get("year", book["year"])
|
||||
book["subscriptionUuid"] = data.get("subscriptionUuid", book["subscriptionUuid"])
|
||||
|
||||
return jsonify(book), 200
|
||||
|
||||
|
||||
@app.route('/books/<uuid:book_id>', methods=['DELETE'])
|
||||
def delete_book(book_id):
|
||||
global books
|
||||
book = next((b for b in books if b['uuid'] == str(book_id)), None)
|
||||
if not book:
|
||||
return jsonify({"error": "Book not found"}), 404
|
||||
|
||||
# Удаляем книгу
|
||||
books = [b for b in books if b['uuid'] != str(book_id)]
|
||||
return jsonify({"message": "Book deleted successfully"}), 200
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5000)
|
3
kosheev_maksim_lab_3/books_service/requirements.txt
Normal file
3
kosheev_maksim_lab_3/books_service/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
flask
|
||||
flask-restful
|
||||
requests
|
28
kosheev_maksim_lab_3/docker-compose.yml
Normal file
28
kosheev_maksim_lab_3/docker-compose.yml
Normal file
@ -0,0 +1,28 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
books_service:
|
||||
build:
|
||||
context: ./books_service
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "5000:5000"
|
||||
|
||||
|
||||
subscriptions_service:
|
||||
build:
|
||||
context: ./subscriptions_service
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "5001:5001"
|
||||
|
||||
|
||||
nginx:
|
||||
image: nginx:latest
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
ports:
|
||||
- "80:80"
|
||||
depends_on:
|
||||
- books_service
|
||||
- subscriptions_service
|
11
kosheev_maksim_lab_3/nginx/nginx.conf
Normal file
11
kosheev_maksim_lab_3/nginx/nginx.conf
Normal file
@ -0,0 +1,11 @@
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location /books/ {
|
||||
proxy_pass http://books_service:5000/;
|
||||
}
|
||||
|
||||
location /subscriptions/ {
|
||||
proxy_pass http://subscriptions_service:5001/;
|
||||
}
|
||||
}
|
8
kosheev_maksim_lab_3/subscriptions_service/Dockerfile
Normal file
8
kosheev_maksim_lab_3/subscriptions_service/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
COPY requirements.txt requirements.txt
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY . .
|
||||
|
||||
CMD ["python", "app.py"]
|
65
kosheev_maksim_lab_3/subscriptions_service/app.py
Normal file
65
kosheev_maksim_lab_3/subscriptions_service/app.py
Normal file
@ -0,0 +1,65 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import uuid
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Данные об абонементах (в памяти)
|
||||
subscriptions = [
|
||||
{
|
||||
"uuid": str(uuid.uuid4()),
|
||||
"number": 135,
|
||||
"fullName": "Иванов И.И.",
|
||||
"issued": "2023-10-18T05:41:00Z"
|
||||
}
|
||||
]
|
||||
|
||||
@app.route('/subscriptions/', methods=['GET'])
|
||||
def get_subscriptions():
|
||||
return jsonify(subscriptions), 200
|
||||
|
||||
@app.route('/subscriptions/<uuid:subscription_id>', methods=['GET'])
|
||||
def get_subscription(subscription_id):
|
||||
subscription = next((s for s in subscriptions if s['uuid'] == str(subscription_id)), None)
|
||||
if subscription:
|
||||
return jsonify(subscription), 200
|
||||
return jsonify({"error": "Subscription not found"}), 404
|
||||
|
||||
@app.route('/subscriptions/', methods=['POST'])
|
||||
def create_subscription():
|
||||
data = request.json
|
||||
new_subscription = {
|
||||
"uuid": str(uuid.uuid4()),
|
||||
"number": data["number"],
|
||||
"fullName": data["fullName"],
|
||||
"issued": data["issued"]
|
||||
}
|
||||
subscriptions.append(new_subscription)
|
||||
return jsonify(new_subscription), 201
|
||||
|
||||
@app.route('/subscriptions/<uuid:subscription_id>', methods=['PUT'])
|
||||
def update_subscription(subscription_id):
|
||||
data = request.json
|
||||
subscription = next((s for s in subscriptions if s['uuid'] == str(subscription_id)), None)
|
||||
if not subscription:
|
||||
return jsonify({"error": "Subscription not found"}), 404
|
||||
|
||||
# Обновляем поля, если они переданы
|
||||
subscription["number"] = data.get("number", subscription["number"])
|
||||
subscription["fullName"] = data.get("fullName", subscription["fullName"])
|
||||
subscription["issued"] = data.get("issued", subscription["issued"])
|
||||
|
||||
return jsonify(subscription), 200
|
||||
|
||||
@app.route('/subscriptions/<uuid:subscription_id>', methods=['DELETE'])
|
||||
def delete_subscription(subscription_id):
|
||||
global subscriptions
|
||||
subscription = next((s for s in subscriptions if s['uuid'] == str(subscription_id)), None)
|
||||
if not subscription:
|
||||
return jsonify({"error": "Subscription not found"}), 404
|
||||
|
||||
# Удаляем запись
|
||||
subscriptions = [s for s in subscriptions if s['uuid'] != str(subscription_id)]
|
||||
return jsonify({"message": "Subscription deleted successfully"}), 200
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5001)
|
@ -0,0 +1,3 @@
|
||||
flask
|
||||
flask-restful
|
||||
requests
|
Loading…
Reference in New Issue
Block a user