DAS_2023_1/romanova_adelina_lab_3
2023-12-12 21:24:00 +04:00
..
nginx romanova_adelina_lab_3 is ready 2023-12-12 21:24:00 +04:00
product_service romanova_adelina_lab_3 is ready 2023-12-12 21:24:00 +04:00
service_service romanova_adelina_lab_3 is ready 2023-12-12 21:24:00 +04:00
docker-compose.yaml romanova_adelina_lab_3 is ready 2023-12-12 21:24:00 +04:00
README.md romanova_adelina_lab_3 is ready 2023-12-12 21:24:00 +04:00

Лабораторная работа №3 - REST API, Gateway и синхронный обмен между микросервисами

Задание

Цель:

Изучение шаблона проектирования gateway, построения синхронного обмена между микросервисами и архитектурного стиля RESTful API.

Задачи:

  • Создать 2 микросервиса, реализующих CRUD на связанных сущностях.
  • Реализовать механизм синхронного обмена сообщениями между микросервисами.
  • Реализовать шлюз на основе прозрачного прокси-сервера nginx.

Сервисы:

 1. product_service - сервис, отвечающий за продукты

 2. service_service - сервис, отвечающий за услуги, которые содержат в себе продукты

Связь между сервисами:

service (single) <- product (many)

Как запустить программу:

docker compose up

Файловая иерархия

Лаба 3/
|-- service_service/
|   |-- Dockerfile
|   |-- requirements.txt
|   |-- service_service.py
|-- product_service/
|   |-- Dockerfile
|   |-- requirements.txt
|   |-- product_service.py
|-- nginx/
|   |-- Dockerfile
|   |-- nginx.conf
|-- docker-compose.yml

Описание работы:

Для разработки приложений был выбран язык программирования - python

Синхронный обмен сообщениями

product_service будет отправлять http-запрос на service_service при определенных crud операциях.

Операции CRUD для услуги

Просмотр списка имеющихся услуг:

@app.route('/all', methods=['GET'])
def show_service_ids():
    return service_ids

Добавление услуги:

@app.route('/add/<string:name>', methods=['POST'])
def create_service_id(name):
    if request.method == 'POST':
        if len(service_ids) == 0:
            id = 1
        else:
            id = service_ids[-1]['id'] + 1
        service_id = {'id': id, 'name': name}
        service_ids.append(service_id)
        return jsonify(service_id)

Поиск услуги по ID:

@app.route('/get/<int:id>', methods=['GET'])
def get_service_id(id):
    found_service = next((service_id for service_id in service_ids if service_id['id'] == id), None)
    if found_service:
        return jsonify(found_service)
    else:
        return jsonify({'error': 'service_id not found'})

Обновление услуги:

@app.route('/upd/<int:id>_<string:name>', methods=['PUT'])
def update_service_id(id, name):
    service = next((service_id for service_id in service_ids if service_id['id'] == id), None)
    if service:
        service['name'] = name
        return jsonify(service)
    else:
        return jsonify({'error': 'service_id not found'})

Удаление услуги:

@app.route('/del/<int:id>', methods=['DELETE'])
def delete_service_id(id):
    global service_ids
    service_ids = [service_id for service_id in service_ids if service_id['id'] != id]
    return jsonify({'result': True})

if __name__ == '__main__':
    app.run(port=5001)

Операции CRUD для продукта

Просмотр списка имеющихся продуктов:

@app.route('/all', methods=['GET'])
def show_products():
    return products

Добавление продукта:

@app.route('/add/<int:service_id>_<string:product>', methods=['POST'])
def create_product(service_id, product):
    product = {'id': len(products) + 1,
             'service_id': service_id,
             'product': product,
             'action': 'create'}
    products.append(product)
    requests.post("http://service-service:5001/event", json=product)

    return jsonify(product)

Поиск продукта по ID:

@app.route('/get/<int:product_id>', methods=['GET'])
def get_product(product_id):
    found_product = next((product for product in products if product['id'] == product_id), None)
    if found_product:
        read_product = {'id': found_product['id'],
                        'service_id': found_product['service_id'],
                        'product': found_product['product']}
        return jsonify(read_product)
    else:
        return jsonify({'error': 'product not found'})

Обновление продукта:

@app.route('/upd/<int:product_id>/<string:new_product>', methods=['PUT'])
def update_product(product_id, new_product):
    product = next((prod for prod in products if prod['id'] == product_id), None)
    if product:
        product['product'] = new_product
        product['action'] = 'update'
        requests.post("http://service-service:5001/event", json=product)
        return jsonify({'message': 'Product updated successfully'})
    else:
        return jsonify({'error': 'Product not found'})

Удаление продукта:

@app.route('/product/del_<int:product_id>', methods=['DELETE'])
def delete_product(product_id):
    global products
    deleted_product = next((product for product in products if product['id'] == product_id), None)
    products = [product for product in products if product['id'] != product_id]
    deleted_product['action'] = 'delete'

    requests.post("http://service-service:5001/event", json=deleted_product)

    return jsonify({'result': True})

Docker-compose

Конфигурационный файл docker-сompose представляет собой многоконтейнерное приложение с тремя сервисами: service-service, product-service и nginx. Эти сервисы подключены к собственной сети типа "bridge" с именем my_network

version: '3.8'

services:
  service-service:
    build:
      context: ./service_service
    ports:
      - "5001:5001"
    networks:
      - my_network
    restart: always

  product-service:
    build:
      context: ./product_service
    ports:
      - "5002:5002"
    networks:
      - my_network
    restart: always

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    depends_on:
      - service-service
      - product-service
    networks:
      - my_network
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    restart: always

networks:
  my_network:
    driver: bridge

Nginx

Этот файл представляет собой конфигурацию для Nginx, который является веб-сервером и обратным прокси:

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        listen [::]:80;
        server_name localhost;

        location /service-service/ {
            proxy_pass http://service-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;
        }

        location /product-service/ {
            proxy_pass http://product-service:5002/;
            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;
        }
    }
}

Dockerfile микросервисов

Dockerfile для услуг

FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app
CMD ["gunicorn", "--bind", "0.0.0.0:5001", "service_service:app"]

Dockerfile для продуктов

FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app
CMD ["gunicorn", "--bind", "0.0.0.0:5002", "product_service:app"]

Youtube

https://www.youtube.com/watch?v=Sf0CkkcuveY