diff --git a/pupkov_alexey_lab_3/README.md b/pupkov_alexey_lab_3/README.md new file mode 100644 index 0000000..5346515 --- /dev/null +++ b/pupkov_alexey_lab_3/README.md @@ -0,0 +1,32 @@ +# Лабораторная работа №3 - REST API, шлюз и синхронный обмен данными между микросервисами + +## Задание + +### Цель: +Изучение принципов проектирования с использованием паттерна шлюза, организации синхронной передачи данных между микросервисами и применения архитектурного стиля RESTful API. + +### Задачи: +1. Создание двух микросервисов, которые реализуют операции CRUD для связанных сущностей. +2. Реализация механизма синхронного обмена данными между микросервисами. +3. Настройка шлюза на базе Nginx в качестве прозрачного прокси-сервера. + +### Микросервисы: +1. book_service — сервис, который управляет информацией о книгах. +2. subscription_service — сервис, который обрабатывает данные о подспиках, которым принадлежащих книги. + +## Описание работы: + +Для разработки микросервисов был выбран язык программирования Python. + +### Синхронный обмен данными +Сервис book_service отправляет HTTP-запросы к subscription_service при выполнении определенных операций CRUD. Это позволяет получать актуальную информацию о предметах, связанных с конкретными героями. + +### Docker Compose +Конфигурационный файл docker-compose.yml представляет собой многоконтейнерное приложение, которое включает в себя три сервиса: book_service, subscription_service и nginx. Функция маршрутизации возложена на сервер Nginx, который обрабатывает запросы и перенаправляет их на соответствующие микросервисы. + +### Nginx +Конфигурационный файл Nginx определяет настройки веб-сервера и обратного прокси, который управляет входящими запросами и направляет их на соответствующие сервисы. + +## Видео ВК + + [Ссылка на демонстрацию работы программы](https://vk.com/video547368103_456239604?list=ln-6CV0kolsFgPBdGrKz7) diff --git a/pupkov_alexey_lab_3/docker-compose.yaml b/pupkov_alexey_lab_3/docker-compose.yaml new file mode 100644 index 0000000..d4a4b7d --- /dev/null +++ b/pupkov_alexey_lab_3/docker-compose.yaml @@ -0,0 +1,25 @@ +version: '3.8' +networks: + default: + name: my_network + +services: + subscription-service: + build: ./services/subscription + ports: + - "5000:5000" + + book-service: + build: ./services/book + ports: + - "5001:5001" + + gateway: + image: nginx:latest + volumes: + - ./gateway/nginx.conf:/etc/nginx/nginx.conf + ports: + - "80:80" + depends_on: + - subscription-service + - book-service diff --git a/pupkov_alexey_lab_3/gateway/nginx.conf b/pupkov_alexey_lab_3/gateway/nginx.conf new file mode 100644 index 0000000..f5828d7 --- /dev/null +++ b/pupkov_alexey_lab_3/gateway/nginx.conf @@ -0,0 +1,32 @@ +events { + worker_connections 1024; +} + +http { + sendfile on; + server { + listen 80; + listen [::]:80; + server_name localhost; + + # Прокси для сервиса подписок + location /api/subscriptions/ { + proxy_pass http://subscription-service:5000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + } + + # Прокси для сервиса книг + location /api/books/ { + proxy_pass http://book-service:5001/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + } + } +} diff --git a/pupkov_alexey_lab_3/services/book/Dockerfile b/pupkov_alexey_lab_3/services/book/Dockerfile new file mode 100644 index 0000000..d479577 --- /dev/null +++ b/pupkov_alexey_lab_3/services/book/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.10-slim +WORKDIR /app +COPY . . +RUN pip install flask flask-restful requests +CMD ["python", "app.py"] diff --git a/pupkov_alexey_lab_3/services/book/app.py b/pupkov_alexey_lab_3/services/book/app.py new file mode 100644 index 0000000..7d90ec0 --- /dev/null +++ b/pupkov_alexey_lab_3/services/book/app.py @@ -0,0 +1,63 @@ +from flask import Flask, request, jsonify +import uuid +import requests + +app = Flask(__name__) + +# Хранилище данных +books = [] + +SUBSCRIPTION_SERVICE_URL = "http://subscription-service:5000/api/subscriptions/" + +@app.route('/api/books/', methods=['GET']) +def get_books(): + return jsonify(books), 200 + +@app.route('/api/books/', methods=['GET']) +def get_book(uuid): + book = next((b for b in books if b['uuid'] == str(uuid)), None) + if not book: + return jsonify({'error': 'Book not found'}), 404 + + subscription_uuid = book['subscriptionUuid'] + subscription = requests.get(f"{SUBSCRIPTION_SERVICE_URL}{subscription_uuid}").json() + book['subscriptionInfo'] = subscription + + return jsonify(book), 200 + +@app.route('/api/books/', methods=['POST']) +def create_book(): + data = request.json + book = { + 'uuid': str(uuid.uuid4()), + 'author': data['author'], + 'subject': data['subject'], + 'year': data['year'], + 'subscriptionUuid': data['subscriptionUuid'] + } + books.append(book) + return jsonify(book), 201 + +@app.route('/api/books/', methods=['PUT']) +def update_book(uuid): + data = request.json + book = next((b for b in books if b['uuid'] == str(uuid)), None) + if not book: + return jsonify({'error': 'Book not found'}), 404 + + book.update({ + 'author': data['author'], + 'subject': data['subject'], + 'year': data['year'], + 'subscriptionUuid': data['subscriptionUuid'] + }) + return jsonify(book), 200 + +@app.route('/api/books/', methods=['DELETE']) +def delete_book(uuid): + global books + books = [b for b in books if b['uuid'] != str(uuid)] + return '', 204 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5001) diff --git a/pupkov_alexey_lab_3/services/subscription/Dockerfile b/pupkov_alexey_lab_3/services/subscription/Dockerfile new file mode 100644 index 0000000..4078349 --- /dev/null +++ b/pupkov_alexey_lab_3/services/subscription/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.10-slim +WORKDIR /app +COPY . . +RUN pip install flask flask-restful +CMD ["python", "app.py"] diff --git a/pupkov_alexey_lab_3/services/subscription/app.py b/pupkov_alexey_lab_3/services/subscription/app.py new file mode 100644 index 0000000..894bbcc --- /dev/null +++ b/pupkov_alexey_lab_3/services/subscription/app.py @@ -0,0 +1,53 @@ +from flask import Flask, request, jsonify +import uuid + +app = Flask(__name__) + +# Хранилище данных +subscriptions = [] + +@app.route('/api/subscriptions/', methods=['GET']) +def get_subscriptions(): + return jsonify(subscriptions), 200 + +@app.route('/api/subscriptions/', methods=['GET']) +def get_subscription(uuid): + subscription = next((sub for sub in subscriptions if sub['uuid'] == str(uuid)), None) + if not subscription: + return jsonify({'error': 'Subscription not found'}), 404 + return jsonify(subscription), 200 + +@app.route('/api/subscriptions/', methods=['POST']) +def create_subscription(): + data = request.json + subscription = { + 'uuid': str(uuid.uuid4()), + 'number': data['number'], + 'fullName': data['fullName'], + 'issued': data['issued'] + } + subscriptions.append(subscription) + return jsonify(subscription), 201 + +@app.route('/api/subscriptions/', methods=['PUT']) +def update_subscription(uuid): + data = request.json + subscription = next((sub for sub in subscriptions if sub['uuid'] == str(uuid)), None) + if not subscription: + return jsonify({'error': 'Subscription not found'}), 404 + + subscription.update({ + 'number': data['number'], + 'fullName': data['fullName'], + 'issued': data['issued'] + }) + return jsonify(subscription), 200 + +@app.route('/api/subscriptions/', methods=['DELETE']) +def delete_subscription(uuid): + global subscriptions + subscriptions = [sub for sub in subscriptions if sub['uuid'] != str(uuid)] + return '', 204 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000)