DAS_2023_1/romanova_adelina_lab_3/README.md

283 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Лабораторная работа №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