bondarenko_max_lab_2 #231
1
.idea/.name
Normal file
@ -0,0 +1 @@
|
||||
main.py
|
12
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredIdentifiers">
|
||||
<list>
|
||||
<option value="str.__pos__" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
4
.idea/misc.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (tukaeva_alfiya_lab_4)" project-jdk-type="Python SDK" />
|
||||
</project>
|
48
artamonova_tatyana_lab_1/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
## Отчет по Docker Compose конфигурации
|
||||
|
||||
### Краткое описание:
|
||||
|
||||
Данная конфигурация Docker Compose запускает набор сервисов, необходимых для работы WordPress и MediaWiki. Она включает в себя:
|
||||
|
||||
- **WordPress:** веб-сервис для блогов и CMS
|
||||
- **MySQL:** база данных для хранения данных WordPress
|
||||
- **RabbitMQ:** брокер сообщений для потенциального использования в будущем
|
||||
- **MediaWiki:** вики-движок для создания и редактирования вики-страниц
|
||||
|
||||
### Запуск лабораторной работы:
|
||||
|
||||
1. Установить Docker и Docker Compose.
|
||||
2. Сохранить конфигурацию в файл docker-compose.yml.
|
||||
3. Запустить команду docker-compose up --build
|
||||
|
||||
### Используемые технологии:
|
||||
|
||||
- **Docker Compose:** инструмент для определения и запуска многоконтейнерных приложений.
|
||||
- **Docker:** платформа для создания, развертывания и запуска контейнеров.
|
||||
- **WordPress:** популярная платформа для создания блогов и CMS.
|
||||
- **MySQL:** популярная система управления базами данных.
|
||||
- **RabbitMQ:** брокер сообщений, используемый для асинхронного обмена сообщениями.
|
||||
- **MediaWiki:** свободное программное обеспечение для создания и редактирования вики-страниц.
|
||||
|
||||
### Функциональность:
|
||||
|
||||
Конфигурация запускает следующие сервисы:
|
||||
|
||||
- **WordPress:** работает на порту 8080, доступен по адресу http://localhost:8080.
|
||||
- **MySQL:** предоставляет базу данных для WordPress и MediaWiki.
|
||||
- **RabbitMQ:** работает на порту 5672, доступен по адресу http://localhost:15672 для управления.
|
||||
- **MediaWiki:** работает на порту 8081, доступен по адресу http://localhost:8081.
|
||||
|
||||
### Дополнительные сведения
|
||||
|
||||
- **Volumes**: используются для хранения данных сервисов, чтобы они не терялись при перезапуске контейнеров.
|
||||
- **Depends_on**: указывает на зависимость между сервисами, например, WordPress зависит от MySQL.
|
||||
- **Restart policy**: определяет, как сервисы будут перезапускаться после сбоя.
|
||||
|
||||
### Видео
|
||||
|
||||
https://vk.com/video/@artamonovat?z=video212084908_456239356%2Fpl_212084908_-2
|
||||
|
||||
### Заключение:
|
||||
|
||||
Данная конфигурация Docker Compose обеспечивает простой и удобный способ запуска и управления несколькими сервисами, связанными с WordPress и MediaWiki. Она позволяет разработчикам легко развертывать и управлять приложениями в изолированной среде.
|
61
artamonova_tatyana_lab_1/docker-compose.yml
Normal file
@ -0,0 +1,61 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
image: wordpress:latest
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- wordpress_data:/var/www/html
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_NAME: wordpress
|
||||
WORDPRESS_DB_USER: wordpress
|
||||
WORDPRESS_DB_PASSWORD: password
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
|
||||
db:
|
||||
image: mysql:latest
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
environment:
|
||||
MYSQL_DATABASE: wordpress
|
||||
MYSQL_USER: wordpress
|
||||
MYSQL_PASSWORD: dbpassword
|
||||
MYSQL_ROOT_PASSWORD: rootpassword
|
||||
restart: unless-stopped
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3-management
|
||||
ports:
|
||||
- "5672:5672"
|
||||
- "15672:15672"
|
||||
volumes:
|
||||
- rabbitmq_data:/var/lib/rabbitmq
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: guest
|
||||
RABBITMQ_DEFAULT_PASS: password
|
||||
restart: unless-stopped
|
||||
|
||||
mediawiki:
|
||||
image: mediawiki:latest
|
||||
ports:
|
||||
- "8081:80"
|
||||
volumes:
|
||||
- mediawiki_data:/var/www/html
|
||||
environment:
|
||||
MW_DB_SERVER: db
|
||||
MW_DB_NAME: mediawiki
|
||||
MW_DB_USER: mediawiki
|
||||
MW_DB_PASSWORD: mediawiki_password
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
wordpress_data:
|
||||
db_data:
|
||||
rabbitmq_data:
|
||||
mediawiki_data:
|
5
artamonova_tatyana_lab_2/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
*.pyc
|
||||
__pycache__
|
||||
*.egg-info
|
||||
*.dist-info
|
||||
.DS_Store
|
22
artamonova_tatyana_lab_2/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
## Лабораторная работа №2
|
||||
### Выполнила Артамонова Татьяна ПИбд-42
|
||||
|
||||
**Вариант 1: Программа 4 - Количество символов в именах файлов из каталога /var/data**
|
||||
|
||||
- Формирует файл /var/result/data1.txt так, что каждая строка файла - количество символов в именах файлов из каталога /var/data.
|
||||
|
||||
**Вариант 2: Программа 3 - Количество чисел в последовательности**
|
||||
|
||||
- Ищет набольшее число из файла /var/result/data1.txt и сохраняет количество таких чисел из последовательности в /var/result/data2.txt.
|
||||
|
||||
**Структура проекта:**
|
||||
|
||||
1. В папках worker-1, worker-2 лежат выполняемые файлы .py и Dockerfile-ы с необходимым набором инструкций.
|
||||
2. В папке data лежат файлы, длину имен которых нужно посчитать.
|
||||
3. В папке result лежат файлы с результатами выполнения программ. data1.txt - результат выполнения main1.py (worker-1), data2.txt - результат выполнения main2.py (worker-2). Данные в data2 рассчитываются из данных data1.
|
||||
4. Файл .gitignore - для указания, какие файлы отслеживать, а какие - нет.
|
||||
5. docker-compose.yml - для определения и управления контейнерами Docker.
|
||||
|
||||
**Команда для запуска** - docker-compose up --build
|
||||
|
||||
**Ссылка на видео:** https://vk.com/artamonovat?z=video212084908_456239357%2Fvideos212084908%2Fpl_212084908_-2
|
0
artamonova_tatyana_lab_2/data/exampleFile.txt
Normal file
0
artamonova_tatyana_lab_2/data/exampleFile123.txt
Normal file
0
artamonova_tatyana_lab_2/data/exampleFile456.txt
Normal file
22
artamonova_tatyana_lab_2/docker-compose.yml
Normal file
@ -0,0 +1,22 @@
|
||||
services:
|
||||
worker-1:
|
||||
build:
|
||||
context: ./worker-1
|
||||
volumes:
|
||||
- ./worker-1:/app
|
||||
- ./data:/var/data
|
||||
- ./result:/var/result
|
||||
depends_on:
|
||||
- worker-2
|
||||
|
||||
worker-2:
|
||||
build:
|
||||
context: ./worker-2
|
||||
volumes:
|
||||
- ./worker-2:/app
|
||||
- ./data:/var/data
|
||||
- ./result:/var/result
|
||||
|
||||
volumes:
|
||||
data:
|
||||
result:
|
3
artamonova_tatyana_lab_2/result/data1.txt
Normal file
@ -0,0 +1,3 @@
|
||||
15
|
||||
18
|
||||
18
|
1
artamonova_tatyana_lab_2/result/data2.txt
Normal file
@ -0,0 +1 @@
|
||||
2
|
14
artamonova_tatyana_lab_2/worker-1/Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
# Используем образ Python 3.10-slim как основу для нашего контейнера.
|
||||
# slim-версия образа более компактная, что делает контейнер меньше.
|
||||
FROM python:3.10-slim
|
||||
|
||||
# Устанавливаем рабочую директорию в контейнере как /app.
|
||||
# Все последующие команды будут выполняться в этой директории.
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем файл main1.py из текущей директории в директорию /app в контейнере.
|
||||
COPY main1.py .
|
||||
|
||||
# Определяем команду, которая будет выполняться при запуске контейнера.
|
||||
# В данном случае запускается Python-скрипт main1.py.
|
||||
CMD ["python", "main1.py"]
|
21
artamonova_tatyana_lab_2/worker-1/main1.py
Normal file
@ -0,0 +1,21 @@
|
||||
import os
|
||||
import glob
|
||||
|
||||
# Формирует файл data1.txt так, что каждая строка файла - кол-во символов в именах файла из каталога /data
|
||||
def main():
|
||||
data_dir = "/var/data"
|
||||
result_file = "/var/result/data1.txt"
|
||||
|
||||
result_dir = os.path.dirname(result_file)
|
||||
if not os.path.exists(result_dir):
|
||||
os.makedirs(result_dir)
|
||||
|
||||
files = glob.glob(os.path.join(data_dir, '*'))
|
||||
|
||||
with open(result_file, 'w') as f:
|
||||
for file in files:
|
||||
filename = os.path.basename(file)
|
||||
f.write(f"{len(filename)}\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
14
artamonova_tatyana_lab_2/worker-2/Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
# Используем образ Python 3.10-slim как основу для нашего контейнера.
|
||||
# slim-версия образа более компактная, что делает контейнер меньше.
|
||||
FROM python:3.10-slim
|
||||
|
||||
# Устанавливаем рабочую директорию в контейнере как /app.
|
||||
# Все последующие команды будут выполняться в этой директории.
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем файл main2.py из текущей директории в директорию /app в контейнере.
|
||||
COPY main2.py .
|
||||
|
||||
# Определяем команду, которая будет выполняться при запуске контейнера.
|
||||
# В данном случае запускается Python-скрипт main2.py.
|
||||
CMD ["python", "main2.py"]
|
26
artamonova_tatyana_lab_2/worker-2/main2.py
Normal file
@ -0,0 +1,26 @@
|
||||
import os
|
||||
|
||||
# Ищет наибольшее число из файла data1.txt и сохраняет количество таких чисел из последовательности в data2.txt
|
||||
def main():
|
||||
data_file_path = "/var/result/data1.txt"
|
||||
result_file_path = "/var/result/data2.txt"
|
||||
|
||||
if not os.path.exists(data_file_path):
|
||||
data_dir = os.path.dirname(data_file_path)
|
||||
|
||||
if not os.path.exists(result_file_path):
|
||||
result_dir = os.path.dirname(result_file_path)
|
||||
|
||||
with open(data_file_path, 'r') as f:
|
||||
numbers = [int(x.strip()) for x in f.read().splitlines()]
|
||||
|
||||
max_number = max(numbers)
|
||||
count = numbers.count(max_number)
|
||||
|
||||
with open(result_file_path, 'w') as f:
|
||||
f.write(str(count))
|
||||
|
||||
print(f"Количество наибольших чисел: {count}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
34
bogdanov_dmitry_lab_4/README.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Богданов Дмитрий ПИбд-42
|
||||
# Лабораторная работа №4
|
||||
|
||||
|
||||
## Предметная область:
|
||||
Автоматизация работы теплицы
|
||||
|
||||
## Результаты выполнения туториалов:
|
||||
|
||||
- Первый туториал:
|
||||
![изображение 1](./images/tut1.png)
|
||||
|
||||
- Второй туториал:
|
||||
![изображение 2](./images/tut2.png)
|
||||
|
||||
- Третий туториал:
|
||||
![изображение 3](./images/tut3.png)
|
||||
|
||||
|
||||
## Данные из RabbitMQ:
|
||||
|
||||
![изображение 1](./images/rmq1.png)
|
||||
![изображение 2](./images/rmq2.png)
|
||||
![изображение 3](./images/rmq3.png)
|
||||
![изображение 3](./images/rmq4.png)
|
||||
|
||||
### Вывод:
|
||||
Из-за моментальной обработки сообщений в Consumer2, его очередь никогда не заполняется.
|
||||
Consumer1 же тратит на обработку 2 секунды, из-за чего соответствующая очередь существенно заполняется при одном
|
||||
запущенном экземпляре.
|
||||
При нескольких запущенных экземплярах Consumer1 очередь заполняется существенно медленнее, и перестаёт заполняться совсем при определенном кол-ве запущенных экземпляров.
|
||||
|
||||
|
||||
## [Видео](https://drive.google.com/file/d/1KWHHYWiK8OX48OfhDnEKDtMz-Umfs0uj/view?usp=sharing)
|
27
bogdanov_dmitry_lab_4/Sample Program/consumer1.py
Normal file
@ -0,0 +1,27 @@
|
||||
import pika
|
||||
import time
|
||||
|
||||
|
||||
def callback(ch, method, properties, body):
|
||||
print(f'Receiver 1: получено сообщение. {body.decode()}')
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
print('Receiver 1 закончил обработку')
|
||||
|
||||
|
||||
def consume_events_1():
|
||||
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=5672, credentials=pika.PlainCredentials("user", "password")))
|
||||
channel = connection.channel()
|
||||
|
||||
channel.queue_declare(queue='receiver1_queue')
|
||||
channel.queue_bind(exchange='greenhouse_events', queue='receiver1_queue')
|
||||
|
||||
channel.basic_consume(queue='receiver1_queue', on_message_callback=callback, auto_ack=True)
|
||||
|
||||
print('Ожидание сообщения...')
|
||||
channel.start_consuming()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
consume_events_1()
|
24
bogdanov_dmitry_lab_4/Sample Program/consumer2.py
Normal file
@ -0,0 +1,24 @@
|
||||
import pika
|
||||
|
||||
|
||||
def callback(ch, method, properties, body):
|
||||
print(f'Receiver 2: получено сообщение. {body.decode()}')
|
||||
|
||||
print('Receiver 2 закончил обработку')
|
||||
|
||||
|
||||
def consume_events_2():
|
||||
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=5672, credentials=pika.PlainCredentials("user", "password")))
|
||||
channel = connection.channel()
|
||||
|
||||
channel.queue_declare(queue='receiver2_queue')
|
||||
channel.queue_bind(exchange='greenhouse_events', queue='receiver2_queue')
|
||||
|
||||
channel.basic_consume(queue='receiver2_queue', on_message_callback=callback, auto_ack=True)
|
||||
|
||||
print('Ожидание сообщения...')
|
||||
channel.start_consuming()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
consume_events_2()
|
25
bogdanov_dmitry_lab_4/Sample Program/publisher.py
Normal file
@ -0,0 +1,25 @@
|
||||
import pika
|
||||
import time
|
||||
|
||||
def publish_events():
|
||||
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=5672, credentials=pika.PlainCredentials("user", "password")))
|
||||
channel = connection.channel()
|
||||
|
||||
channel.exchange_declare(exchange='greenhouse_events', exchange_type='fanout')
|
||||
|
||||
events = [
|
||||
"Влажность превысила верхнюю границу",
|
||||
"Влажность упала за нижнюю границу",
|
||||
"Полив начат",
|
||||
"Полив остановлен"
|
||||
]
|
||||
|
||||
while True:
|
||||
event = events[int(time.time()) % len(events)]
|
||||
channel.basic_publish(exchange='greenhouse_events', routing_key='', body=event)
|
||||
print(f'Отправлено: {event}')
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
publish_events()
|
25
bogdanov_dmitry_lab_4/Tutorial 1/receive.py
Normal file
@ -0,0 +1,25 @@
|
||||
import pika, sys, os
|
||||
|
||||
def main():
|
||||
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=5672, credentials=pika.PlainCredentials("user", "password")))
|
||||
channel = connection.channel()
|
||||
|
||||
channel.queue_declare(queue='hello')
|
||||
|
||||
def callback(ch, method, properties, body):
|
||||
print(f" [x] Received {body}")
|
||||
|
||||
channel.basic_consume(queue='hello', on_message_callback=callback, auto_ack=True)
|
||||
|
||||
print(' [*] Waiting for messages. To exit press CTRL+C')
|
||||
channel.start_consuming()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print('Interrupted')
|
||||
try:
|
||||
sys.exit(0)
|
||||
except SystemExit:
|
||||
os._exit(0)
|
13
bogdanov_dmitry_lab_4/Tutorial 1/send.py
Normal file
@ -0,0 +1,13 @@
|
||||
import pika
|
||||
|
||||
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=5672, credentials=pika.PlainCredentials("user", "password")))
|
||||
channel = connection.channel()
|
||||
|
||||
channel.queue_declare('hello')
|
||||
|
||||
channel.basic_publish(exchange='',
|
||||
routing_key='hello',
|
||||
body='Hello world!')
|
||||
print(" [x] Sent 'Hello world!'")
|
||||
|
||||
connection.close()
|
19
bogdanov_dmitry_lab_4/Tutorial 2/new_task.py
Normal file
@ -0,0 +1,19 @@
|
||||
import pika
|
||||
import sys
|
||||
|
||||
connection = pika.BlockingConnection(
|
||||
pika.ConnectionParameters(host='localhost', port=5672, credentials=pika.PlainCredentials("user", "password")))
|
||||
channel = connection.channel()
|
||||
|
||||
channel.queue_declare(queue='task_queue', durable=True)
|
||||
|
||||
message = ' '.join(sys.argv[1:]) or "Hello World!"
|
||||
channel.basic_publish(
|
||||
exchange='',
|
||||
routing_key='task_queue',
|
||||
body=message,
|
||||
properties=pika.BasicProperties(
|
||||
delivery_mode=pika.DeliveryMode.Persistent
|
||||
))
|
||||
print(f" [x] Sent {message}")
|
||||
connection.close()
|
22
bogdanov_dmitry_lab_4/Tutorial 2/worker.py
Normal file
@ -0,0 +1,22 @@
|
||||
import pika
|
||||
import time
|
||||
|
||||
connection = pika.BlockingConnection(
|
||||
pika.ConnectionParameters(host='localhost', port=5672, credentials=pika.PlainCredentials("user", "password")))
|
||||
channel = connection.channel()
|
||||
|
||||
channel.queue_declare(queue='task_queue', durable=True)
|
||||
print(' [*] Waiting for messages. To exit press CTRL+C')
|
||||
|
||||
|
||||
def callback(ch, method, properties, body):
|
||||
print(f" [x] Received {body.decode()}")
|
||||
time.sleep(body.count(b'.'))
|
||||
print(" [x] Done")
|
||||
ch.basic_ack(delivery_tag=method.delivery_tag)
|
||||
|
||||
|
||||
channel.basic_qos(prefetch_count=1)
|
||||
channel.basic_consume(queue='task_queue', on_message_callback=callback)
|
||||
|
||||
channel.start_consuming()
|
13
bogdanov_dmitry_lab_4/Tutorial 3/emit_log.py
Normal file
@ -0,0 +1,13 @@
|
||||
import pika
|
||||
import sys
|
||||
|
||||
connection = pika.BlockingConnection(
|
||||
pika.ConnectionParameters(host='localhost', port=5672, credentials=pika.PlainCredentials("user", "password")))
|
||||
channel = connection.channel()
|
||||
|
||||
channel.exchange_declare(exchange='logs', exchange_type='fanout')
|
||||
|
||||
message = ' '.join(sys.argv[1:]) or "info: Hello World!"
|
||||
channel.basic_publish(exchange='logs', routing_key='', body=message)
|
||||
print(f" [x] Sent {message}")
|
||||
connection.close()
|
22
bogdanov_dmitry_lab_4/Tutorial 3/receive_logs.py
Normal file
@ -0,0 +1,22 @@
|
||||
import pika
|
||||
|
||||
connection = pika.BlockingConnection(
|
||||
pika.ConnectionParameters(host='localhost', port=5672, credentials=pika.PlainCredentials("user", "password")))
|
||||
channel = connection.channel()
|
||||
|
||||
channel.exchange_declare(exchange='logs', exchange_type='fanout')
|
||||
|
||||
result = channel.queue_declare(queue='', exclusive=True)
|
||||
queue_name = result.method.queue
|
||||
|
||||
channel.queue_bind(exchange='logs', queue=queue_name)
|
||||
|
||||
print(' [*] Waiting for logs. To exit press CTRL+C')
|
||||
|
||||
def callback(ch, method, properties, body):
|
||||
print(f" [x] {body}")
|
||||
|
||||
channel.basic_consume(
|
||||
queue=queue_name, on_message_callback=callback, auto_ack=True)
|
||||
|
||||
channel.start_consuming()
|
12
bogdanov_dmitry_lab_4/compose.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
rabbitmq:
|
||||
image: rabbitmq:3-management
|
||||
container_name: rabbitmq
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: user
|
||||
RABBITMQ_DEFAULT_PASS: password
|
||||
ports:
|
||||
- "5672:5672"
|
||||
- "15672:15672"
|
BIN
bogdanov_dmitry_lab_4/images/rmq1.png
Normal file
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
BIN
bogdanov_dmitry_lab_4/images/rmq2.png
Normal file
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
BIN
bogdanov_dmitry_lab_4/images/rmq3.png
Normal file
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
BIN
bogdanov_dmitry_lab_4/images/rmq4.png
Normal file
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
BIN
bogdanov_dmitry_lab_4/images/tut1.png
Normal file
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
BIN
bogdanov_dmitry_lab_4/images/tut2.png
Normal file
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
BIN
bogdanov_dmitry_lab_4/images/tut3.png
Normal file
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
88
bogdanov_dmitry_lab_5/MatrixMult/main.py
Normal file
@ -0,0 +1,88 @@
|
||||
import random as rnd
|
||||
import threading
|
||||
import time
|
||||
from multiprocessing import Pool
|
||||
|
||||
def generateSquareMatrix(size):
|
||||
return [[rnd.randint(0, 100) for i in range(size)] for j in range(size)]
|
||||
|
||||
def printMatrix(matrix):
|
||||
for row in matrix:
|
||||
print(*row, sep="\t")
|
||||
|
||||
|
||||
# Перемножение без использования потоков
|
||||
def matrixMultiplyStandard(matrix1, matrix2):
|
||||
l1 = len(matrix1)
|
||||
l2 = len(matrix2)
|
||||
global result_matrix
|
||||
result = result_matrix
|
||||
for i in range(l1):
|
||||
for j in range(l2):
|
||||
for k in range(l2):
|
||||
result[i][j] += matrix1[i][k] * matrix2[k][j]
|
||||
|
||||
return result
|
||||
|
||||
result_matrix = [[0 for i in range(500)] for j in range(500)]
|
||||
|
||||
# Перемножение в отдельном потоке
|
||||
def matrixMultiplySingleThread(args):
|
||||
matrix1, matrix2, start_i, end_i = args
|
||||
global result_matrix
|
||||
|
||||
result = result_matrix
|
||||
|
||||
for i in range(start_i, end_i):
|
||||
for j in range(len(matrix2[0])):
|
||||
for k in range(len(matrix2)):
|
||||
result[i][j] += matrix1[i - start_i][k] * matrix2[k][j]
|
||||
|
||||
# Параллельное перемножение, использует ф-ю выше для каждого потока
|
||||
def matrixMultiplyWithThreads(matrix1, matrix2, thread_count):
|
||||
l1 = len(matrix1)
|
||||
l2 = len(matrix2)
|
||||
|
||||
# Кол-во строк на последний поток, если деление по потокам будет неточным
|
||||
last_rows_count = 0
|
||||
|
||||
if l1 % thread_count == 0:
|
||||
rows_per_thread = l1 // thread_count
|
||||
else:
|
||||
rows_per_thread = l1 // thread_count
|
||||
last_rows_count = l1 % thread_count
|
||||
|
||||
for i in range(thread_count):
|
||||
start_i = i * rows_per_thread
|
||||
|
||||
if (i - 1) == thread_count and last_rows_count > 0:
|
||||
end_i = start_i + last_rows_count
|
||||
else:
|
||||
end_i = start_i + rows_per_thread
|
||||
|
||||
args = []
|
||||
args.append((matrix1[start_i:end_i], matrix2, start_i, end_i))
|
||||
with Pool(processes = thread_count) as pool:
|
||||
pool.map(matrixMultiplySingleThread, args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
sizes = [100, 300, 500]
|
||||
num_threads = [1, 5, 8, 12]
|
||||
|
||||
for size in sizes:
|
||||
matrix1 = generateSquareMatrix(size)
|
||||
matrix2 = generateSquareMatrix(size)
|
||||
start_time = time.time()
|
||||
matrixMultiplyStandard(matrix1, matrix2)
|
||||
end_time = time.time()
|
||||
print(f"Standard size {size}: {end_time - start_time}s")
|
||||
|
||||
for threads in num_threads:
|
||||
start_time = time.time()
|
||||
matrixMultiplyWithThreads(matrix1, matrix2, threads)
|
||||
end_time = time.time()
|
||||
print(f"Parallel size {size}, {threads} thread(s): {end_time - start_time}s")
|
||||
|
||||
print("-" * 100)
|
18
bogdanov_dmitry_lab_5/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Богданов Дмитрий ПИбд-42
|
||||
# Лабораторная работа №5
|
||||
|
||||
|
||||
## Функционал:
|
||||
- Были созданы методы генерации и отображения матриц заданного размера
|
||||
- Былы созданы методы для параллельного умножения матриц с использованием Pool
|
||||
- Был написан код для бенчмаркинга стандартного и параллельного перемножений
|
||||
|
||||
## Результаты выполнения:
|
||||
|
||||
![изображение 1](./images/Screenshot_1.png)
|
||||
|
||||
### Вывод:
|
||||
Использование нескольких потоков приносит значительный выигрыш только на крупных матрицах, в то время как на матрицах меньшего размера больше времени уходит на менеджмент потоков. Это особенно заметно при сравнении результатов выполнения вычислений на матрице размером 100х100.
|
||||
|
||||
|
||||
## [Видео](https://drive.google.com/file/d/1iPfLjzLiWwmszPH_KJ40vFCX-iWDLm1S/view?usp=sharing)
|
BIN
bogdanov_dmitry_lab_5/images/Screenshot_1.png
Normal file
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
37
borschevskaya_anna_lab_7/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Отчет. Лабораторная работа 7
|
||||
|
||||
## Балансировка нагрузки в распределённых системах при помощи открытых технологий на примерах
|
||||
|
||||
### Какие алгоритмы и методы используются для балансировки нагрузки?
|
||||
В распределенных системах балансировка нагрузки может осуществляться на разных уровнях:
|
||||
- балансировка нагрузки на сетевом уровне
|
||||
- балансировка нагрузки на транспортном уровне
|
||||
- балансировка нагрузки на прикладном уровне
|
||||
|
||||
На сетевом уровне балансировка может реализовываться с помощью таких алгоритмов, как Round Robin, Weighted Round Robin или Least Connections.
|
||||
Алгоритм Round Robin основывается на принципе выделения одному доменному имени несколько IP, которые выбираются
|
||||
при поступлении запроса по очереди.
|
||||
|
||||
Weighted Round Robin - усовершенствованный алгоритм Round Robin, который подразумевает указание весов каждому серверу
|
||||
в зависимости от доступных мощностей.
|
||||
|
||||
Алгоритм Least Connections вносит в предыдущий алгоритм еще одно условие выбора сервера - количество активных подключений к нему,
|
||||
избегая перегруженности имеющихся узлов.
|
||||
|
||||
### Какие открытые технологии существуют для балансировки нагрузки?
|
||||
Среди самых популярных открытых технологий можно выделить Nginx и Kubernetes. Nginx позволяет управлять нагрузкой на
|
||||
компоненты системы с помощью различных алгоритмов и перенаправляет запросы от клиента к сервисам, выстпая в качестве реверс-прокси.
|
||||
Kubernetes представляет собой платформу для управления контейнерами, которая включает встроенные механизмы для балансировки
|
||||
нагрузки между подами.
|
||||
### Как осуществляется балансировка нагрузки на базах данных?
|
||||
|
||||
Можно уменьшить нагрузку на базу данных путем использования механизма кэширования на уровне приложения
|
||||
или с помощью нереляционных БД (Redis и др.). Или с помощью шардирования данных, размещенных в базе.
|
||||
|
||||
Для балансировки нагрузки на базах данных на прикладном уровне, например, существует утилита pgpool —
|
||||
прокси между клиентом и сервером СУБД PostgreSQL, с помощью которого задаются правила перенаправления
|
||||
запросов к БД в зависимости от их содержания (чтение/запись).
|
||||
### Реверс-прокси как один из элементов балансировки нагрузки.
|
||||
Реверс-прокси перенаправляет входящие запросы на соотвествующие сервисы, может выступать при этом и в качестве балансировщика нагрузки, реализуя один из ранее озвученных алгоритмов.
|
||||
Как уже было сказано, примером такого компонента РС может выступать Nginx, в котором с помощью настройки конфигурационного файла можно указать как настройки проксирования,
|
||||
так и добавить логику балансировки.
|
30
borschevskaya_anna_lab_8/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Отчет. Лабораторная работа 8
|
||||
|
||||
### Популярность распределенных систем (РС)
|
||||
Распределенные системы обладают рядом преимуществ, которые побуждают разработчиков адаптировать уже существующие решения к такой архитектуре (и переписывать монолит на микросервисы).
|
||||
Данный подход позволяет сделать систему более отказоустойчивой, так как её компоненты становятся независимыми друг от друга.
|
||||
Особенно это важно для сложных систем, для которых появляется возможность масштабировать отдельные узлы РС.
|
||||
Также при разработке распределенных систем удобно распределяются задачи между командами разработки и есть возможность
|
||||
использовать свой стек технологий для каждого сервиса.
|
||||
|
||||
### Системы оркестрации
|
||||
Системы оркестрации автоматизируют развертывание, масштабирование и управление контейнерами, что значительно упрощает
|
||||
разработку и сопровождение. Однако вместе с тем они накладывают определенные требования к участникам команды разработки,
|
||||
которым необходимо иметь хотя бы базовые знания об используемой технологии.
|
||||
|
||||
### Очереди сообщений
|
||||
Очереди обработки сообщений позволяют организовать асинхронное взаимодействие между компонентами системы.
|
||||
Такой подход важен, если процесс при отправке сообщения не должен блокироваться, а ответ необязательно должен быть
|
||||
получен и обработан мгновенно. Сообщения, которые передаются по очередям, могут быть некоторыми событиями или командами,
|
||||
на которые могут отреагировать сразу несколько сервисов, подписанных на очередь.
|
||||
|
||||
### Преимущества и недостатки РС
|
||||
К преимуществам РС, как уже было сказано выше, можно отнести масштабируемость, отказоустойчивость, гибкость внедрения новых функциональностей,
|
||||
а к недостаткам - сложности отладки возникающих проблем и тестирования,
|
||||
возникающие сетевые задержки при прохождении запроса через несколько сервисов.
|
||||
|
||||
### Параллелизм: за и против
|
||||
Параллельные вычисления нужно применять там, где это действительно нужно. Например, при обработке больших данных, при возможности
|
||||
разбиения задачи на несколько независимых подзадач.
|
||||
Однако в случаях, где важна последовательность операций,
|
||||
параллелизм может привести к усложнению логики приложения и ошибкам, а при малом объеме данных для обработки параллелизм может только ухудшить производительность.
|
12
dozorova_alena_lab_6/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
/dozorova_alena_lab_2/.vs
|
||||
/dozorova_alena_lab_2/ConsoleApp1/.vs
|
||||
/dozorova_alena_lab_2/ConsoleApp1/bin
|
||||
/dozorova_alena_lab_2/ConsoleApp1/obj
|
||||
/dozorova_alena_lab_2/ConsoleApp1/Properties/PublishProfiles
|
||||
/dozorova_alena_lab_2/ConsoleApp2/.vs
|
||||
/dozorova_alena_lab_2/ConsoleApp2/bin
|
||||
/dozorova_alena_lab_2/ConsoleApp2/obj
|
||||
/dozorova_alena_lab_6/ConsoleApp1/.vs
|
||||
/dozorova_alena_lab_6/ConsoleApp1/bin
|
||||
/dozorova_alena_lab_6/ConsoleApp1/obj
|
10
dozorova_alena_lab_6/ConsoleApp1/ConsoleApp1.csproj
Normal file
@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
25
dozorova_alena_lab_6/ConsoleApp1/ConsoleApp1.sln
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.10.35004.147
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp1", "ConsoleApp1.csproj", "{29269567-7466-4C99-BEEF-F5766BDDFB24}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{29269567-7466-4C99-BEEF-F5766BDDFB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{29269567-7466-4C99-BEEF-F5766BDDFB24}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{29269567-7466-4C99-BEEF-F5766BDDFB24}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{29269567-7466-4C99-BEEF-F5766BDDFB24}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {EDED6E1D-0A86-43F9-94EA-6ADCC1FA1B42}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
55
dozorova_alena_lab_6/ConsoleApp1/Extention.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ConsoleApp1
|
||||
{
|
||||
public static class Extention
|
||||
{
|
||||
public static int[,] CreateMatrixWithoutColumn(this int[,] matrix, int column)
|
||||
{
|
||||
var result = new int[matrix.GetLength(0), matrix.GetLength(1) - 1];
|
||||
for (int i = 0; i < matrix.GetLength(0); i++)
|
||||
{
|
||||
for (int j = 0; j < matrix.GetLength(1) - 1; j++)
|
||||
{
|
||||
result[i, j] = j < column ? matrix[i, j] : matrix[i, j + 1];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int[,] CreateMatrixWithoutRow(this int[,] matrix, int row)
|
||||
{
|
||||
var result = new int[matrix.GetLength(0) - 1, matrix.GetLength(1)];
|
||||
for (int i = 0; i < matrix.GetLength(0) - 1; i++)
|
||||
{
|
||||
for (int j = 0; j < matrix.GetLength(1); j++)
|
||||
{
|
||||
result[i, j] = i < row ? matrix[i, j] : matrix[i + 1, j];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static double CalculateDeterminant(this int[,] matrix)
|
||||
{
|
||||
if (matrix.GetLength(0) == 2)
|
||||
{
|
||||
return matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0];
|
||||
}
|
||||
double result = 0;
|
||||
for (var j = 0; j < matrix.GetLength(0); j++)
|
||||
{
|
||||
result += (j % 2 == 1 ? 1 : -1) * matrix[1, j] *
|
||||
matrix.CreateMatrixWithoutColumn(j).CreateMatrixWithoutRow(1).CalculateDeterminant();
|
||||
}
|
||||
//Console.WriteLine("Ко мне пришли с размером " + matrix.GetLength(0));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
87
dozorova_alena_lab_6/ConsoleApp1/Program.cs
Normal file
@ -0,0 +1,87 @@
|
||||
|
||||
using ConsoleApp1;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
internal class Program
|
||||
{
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
var value = new int[3] {100, 300, 500 };
|
||||
|
||||
foreach(var i in value)
|
||||
{
|
||||
var a = CreateMatrix(i, i);
|
||||
var b = CreateMatrix(i, i);
|
||||
|
||||
List<long> times = new() {};
|
||||
Console.WriteLine("Для пяти потоков: ");
|
||||
for (int j = 1; j <= 5; j++)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
Calculate(a, j);
|
||||
|
||||
sw.Stop();
|
||||
times.Add(sw.ElapsedTicks);
|
||||
}
|
||||
Console.WriteLine("Количество тиков для вычисления матрицы стороной "+i+": "+string.Join("\t", times));
|
||||
|
||||
Console.WriteLine("Для десяти потоков: ");
|
||||
for (int j = 1; j <= 10; j++)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
Calculate(a, j);
|
||||
|
||||
sw.Stop();
|
||||
times.Add(sw.ElapsedTicks);
|
||||
}
|
||||
Console.WriteLine("Количество тиков для вычисления матрицы стороной " + i + ": " + string.Join("\t", times));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static int[,] CreateMatrix(int x, int y)
|
||||
{
|
||||
var rnd = new Random();
|
||||
|
||||
var res = new int[y, x];
|
||||
|
||||
for (int i = 0; i < y; i++)
|
||||
{
|
||||
for (int j = 0; j < x; j++)
|
||||
{
|
||||
res[i, j] = rnd.Next(0, 100);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static double Calculate(int[,] matrix, int maxTask)
|
||||
{
|
||||
double res = 0;
|
||||
|
||||
var semaphore = new SemaphoreSlim(maxTask, maxTask);
|
||||
|
||||
for (var j = 0; j < matrix.GetLength(0) - 1; j++)
|
||||
{
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
semaphore.Wait();
|
||||
res += (j % 2 == 1 ? 1 : -1) * matrix[1, j] *
|
||||
matrix.CreateMatrixWithoutColumn(j).
|
||||
CreateMatrixWithoutRow(1).CalculateDeterminant();
|
||||
}
|
||||
finally { semaphore.Release(); }
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
semaphore.Wait(maxTask);
|
||||
return res;
|
||||
}
|
||||
}
|
18
dozorova_alena_lab_6/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Лабораторная работа 6
|
||||
В рамках данной работы мы изучаем выигрыш при распаралелливании процесса вычисления определителя матрицы
|
||||
## Описание
|
||||
Для вычисления определителя мы используем следующую формулу:
|
||||
![alt text](image.png)
|
||||
где ![alt text](image-1.png) - определитель матрицы, полученной из исходной вырезанием 1 строки и j столбца.
|
||||
|
||||
## Запуск
|
||||
По опыту прошлой лабораторной работы, в консольном приложении был реализован алгоритм вычисление детерминанта и запущено сравнение затраченного времени (в тиках) для 5 и 10 потоков.
|
||||
## Результаты
|
||||
Результаты:
|
||||
<br/>
|
||||
![Результат](image-2.png)
|
||||
<br/>
|
||||
Как мы видим, подтверждаются выводы прошлой лабораторной работы: для небольших матриц выигрыш несущественнен из-за затраты времени на работу с потоком, а для больших эта разница в скорости уже существенна
|
||||
|
||||
## Видеодемонстрация
|
||||
Видеодемонстрация по [адресу](https://drive.google.com/file/d/1dOMaRcTRiPwhn2E4ok1WUOeh_dD9NyDQ/view?usp=sharing)
|
BIN
dozorova_alena_lab_6/image-1.png
Normal file
Before Width: | Height: | Size: 556 B After Width: | Height: | Size: 556 B |
BIN
dozorova_alena_lab_6/image-2.png
Normal file
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
BIN
dozorova_alena_lab_6/image.png
Normal file
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
17
dozorova_alena_lab_7/readme.md
Normal file
@ -0,0 +1,17 @@
|
||||
**Балансировка нагрузки** предполагает равномерную нагрузку вычислительных узлов (процессора многопроцессорной ЭВМ или компьютера в сети)
|
||||
|
||||
Следует различать _статическую_ и _динамическую_ балансировки, где первая выполняется до начала выполнения распределенного приложения, а вторая – в процессе.
|
||||
Приведем примеры алгоритмов балансировки:
|
||||
* *Круговой алгоритм*: - распределение входящих запросов между несколькими серверами в порядке циклической очередности. Модификации:
|
||||
- *Взвешенный циклический перебор*, учитывающий мощность серверов
|
||||
- *Динамический круговой алгоритм*, который учитывает текущую нагрузку на серверы при распределении запросов.
|
||||
* *Наименьшее количество соединений* - направление входящих запросов на сервер с наименьшим количеством активных соединений в данный момент времени. Модификации:
|
||||
- *Взвешенное наименьшее количество соединений* – направляет запросы на сервер с наименьшим соотношением активных соединений к его назначенному весу.
|
||||
* *Наименьшее время отклика* - направление запросов на сервер, который демонстрируют наилучшую производительность в данный момент. Он учитывает два ключевых фактора: время отклика сервера и количество активных соединений.
|
||||
* *Наименьший объем трафика* – динамический алгоритм балансировки нагрузки, который направляет входящие запросы на сервер, передающий наименьший объем данных в текущий момент.
|
||||
|
||||
Существует достаточно много различных технологий как для программной, так и аппаратной балансировки. Наиболее популярные решения – это Nginx и OpenELB, MetalLB для Kubernetes.
|
||||
|
||||
Для обеспечения балансировка нагрузки на базах данных используется механизм **репликации**. Балансировка нагрузки заключается в распределении запросов от пользователей по разным копиям (репликам) базы данных.
|
||||
|
||||
**Реверс-прокси** — это сервер, который находится перед веб-серверами и пересылает запросы от клиента на эти веб-серверы. Он не является в полной мере балансировщиком, но может выполнять его функцию при большой нагрузке на систему.
|
12
dozorova_alena_lab_8/readme.md
Normal file
@ -0,0 +1,12 @@
|
||||
Распределенные системы все больше набирают популярность в наше время. Этому способствуют такие ее качества как:
|
||||
* *Устойчивость и масштабируемость* – возможность репликации позволяет поддерживать в работоспособном состоянии всю систему при отказе одного из ее компонентов
|
||||
* *Принцип единой ответственности* – реализация каждого элемента системы в виде сервиса, ответственного за что-то одно, упрощает поддержку и дальнейшее развитие системы.
|
||||
* *Увеличение производительности* – использование одновременно несколько распределенных сервисов позволяет повысить скорость обработки данных
|
||||
|
||||
**Оркестраторы** призваны обеспечивать мониторинг за состоянием системы и выполнять функции по ее оптимизации: организация быстрого развертывания, балансировка нагрузки. Оркестраторы так же позволяют уведомлять пользователей о возникших проблемах и выполнять сбор, агрегацию и анализ логов. Подобная обработка данных позволяет выявить узкие места системы. Но оркестратор так же требует ресурсы на собственную работу.
|
||||
|
||||
Под **сообщениями** подразумеваются данные, которыми обмениваются сервисы внутри системы. **Очереди сообщений** обеспечивают асинхронный обмен информацией. Сервис, отправляющий сообщение, взаимодействует не с другим сервисом, а с очередью и ждет ответа, не блокируя собственные процессы. Брокеры сообщений повышают отказоустойчивость такого обмена – они гарантируют доставку сообщения до сервиса-приемника.
|
||||
|
||||
На мой взгляд основными *преимуществами* распределенных приложений являются их масштабируемость и отказоустойчивость. Но в то же время они имеют и достаточно весомые *недостатки* – сложность их поддержки и реализации, а также сложность обработки и трассировки транзакций.
|
||||
|
||||
**Параллельные вычисления** в подобных системах следует внедрять только в том случае, когда *выигрыш от скорости вычислений значительно повышает затраты на выделение ресурсов*. Вычислить математические характеристики матрицы, к примеру, может быть оправдано, а вот разнесение высокоуровневой логики может быть неоправданно
|
27
emelyanov_artem_lab_6/Benchmark.java
Normal file
@ -0,0 +1,27 @@
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class Benchmark {
|
||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||
int[] sizes = {5, 7, 10};
|
||||
|
||||
for (int size : sizes) {
|
||||
int[][] matrix = MatrixGenerator.generateMatrix(size);
|
||||
|
||||
// Последовательное вычисление
|
||||
long startSequential = System.nanoTime();
|
||||
double detSequential = DeterminantCalculator.calculateDeterminant(matrix, 1);
|
||||
long endSequential = System.nanoTime();
|
||||
System.out.println("Последовательно (" + size + "x" + size + "): " + (endSequential - startSequential) / 1_000_000 + " ms, детерминант: " + detSequential);
|
||||
|
||||
// Параллельное вычисление
|
||||
for (int numThreads : new int[]{2, 4, 8}) {
|
||||
long startParallel = System.nanoTime();
|
||||
double detParallel = DeterminantCalculator.calculateDeterminant(matrix, numThreads);
|
||||
long endParallel = System.nanoTime();
|
||||
System.out.println("Параллельно (" + size + "x" + size + ", " + numThreads + " потоков): " +
|
||||
(endParallel - startParallel) / 1_000_000 + " ms, детерминант: " + detParallel);
|
||||
}
|
||||
System.out.println("--------------------------------------------------");
|
||||
}
|
||||
}
|
||||
}
|
80
emelyanov_artem_lab_6/DeterminantCalculator.java
Normal file
@ -0,0 +1,80 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class DeterminantCalculator {
|
||||
public static int calculateDeterminant(int[][] matrix, int numThreads) throws InterruptedException, ExecutionException {
|
||||
int size = matrix.length;
|
||||
|
||||
// Если размер матрицы 1x1, возвращаем единственный элемент.
|
||||
if (size == 1) {
|
||||
return matrix[0][0];
|
||||
}
|
||||
|
||||
// Если количество потоков равно 1, выполняем последовательный алгоритм.
|
||||
if (numThreads == 1) {
|
||||
return sequentialDeterminant(matrix);
|
||||
}
|
||||
|
||||
// Иначе выполняем параллельный алгоритм.
|
||||
return parallelDeterminant(matrix, numThreads);
|
||||
}
|
||||
|
||||
private static int sequentialDeterminant(int[][] matrix) {
|
||||
int size = matrix.length;
|
||||
if (size == 1) {
|
||||
return matrix[0][0];
|
||||
}
|
||||
if (size == 2) {
|
||||
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
|
||||
}
|
||||
|
||||
int determinant = 0;
|
||||
for (int col = 0; col < size; col++) {
|
||||
determinant += (int) (Math.pow(-1, col) * matrix[0][col] * sequentialDeterminant(getMinor(matrix, 0, col)));
|
||||
}
|
||||
return determinant;
|
||||
}
|
||||
|
||||
private static int parallelDeterminant(int[][] matrix, int numThreads) throws InterruptedException, ExecutionException {
|
||||
int size = matrix.length;
|
||||
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
|
||||
List<Future<Double>> futures = new ArrayList<>();
|
||||
|
||||
for (int col = 0; col < size; col++) {
|
||||
int finalCol = col;
|
||||
futures.add(executor.submit(() -> {
|
||||
double minorDet = sequentialDeterminant(getMinor(matrix, 0, finalCol));
|
||||
return Math.pow(-1, finalCol) * matrix[0][finalCol] * minorDet;
|
||||
}));
|
||||
}
|
||||
|
||||
int determinant = 0;
|
||||
for (Future<Double> future : futures) {
|
||||
determinant += future.get();
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
return determinant;
|
||||
}
|
||||
|
||||
private static int[][] getMinor(int[][] matrix, int row, int col) {
|
||||
int size = matrix.length;
|
||||
int[][] minor = new int[size - 1][size - 1];
|
||||
|
||||
for (int i = 0, mi = 0; i < size; i++) {
|
||||
if (i == row) continue;
|
||||
for (int j = 0, mj = 0; j < size; j++) {
|
||||
if (j == col) continue;
|
||||
minor[mi][mj] = matrix[i][j];
|
||||
mj++;
|
||||
}
|
||||
mi++;
|
||||
}
|
||||
return minor;
|
||||
}
|
||||
}
|
||||
|
11
emelyanov_artem_lab_6/MatrixGenerator.java
Normal file
@ -0,0 +1,11 @@
|
||||
public class MatrixGenerator {
|
||||
public static int[][] generateMatrix(int size) {
|
||||
int[][] matrix = new int[size][size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
matrix[i][j] = (int) (Math.random() * 100);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
}
|
12
emelyanov_artem_lab_6/Readme.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Поиск детерминанта
|
||||
|
||||
Данная работа посвящена реализации и сравнению последовательного и параллельного алгоритмов поиска детерминанта матриц на языке Java. Целью является оценка производительности при использовании разного числа потоков.
|
||||
|
||||
## Результаты:
|
||||
|
||||
![img.png](images/img.png)
|
||||
|
||||
Как видим, однозначно сказать нельзя, для маленьких матриц многопоточность особо не повлияла. Начиная с матрицы размерностью 10, есть прибавка в производительности. Посмотрим на сложность алгоритма, и она будет что-то около O(n!), так как алгоритм рекурсивный.
|
||||
Для больших матриц, типа 100 на 100, сложность будет огромной, и не думаю что такое получится посчитать.
|
||||
|
||||
Ссылка на видео: https://drive.google.com/file/d/1eCNcSLLLfWGlOk5Z0y0CfOfkbxeASrdE/view?usp=sharing
|
BIN
emelyanov_artem_lab_6/images/img.png
Normal file
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
BIN
emelyanov_artem_lab_6/img.png
Normal file
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
80
emelyanov_artem_lab_7/.idea/workspace.xml
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="ece3d3db-34eb-42c6-9dfd-726739d49f9f" name="Changes" comment="feature: completed lab 7" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
||||
<map>
|
||||
<entry key="$PROJECT_DIR$/.." value="emelyanov_artem_lab_7" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
||||
</component>
|
||||
<component name="ProjectColorInfo"><![CDATA[{
|
||||
"associatedIndex": 4
|
||||
}]]></component>
|
||||
<component name="ProjectId" id="2nes6IK0Z3YgtIz3E1743Ralffw" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"git-widget-placeholder": "emelyanov__artem__lab__6",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "/home/forever/УлГТУ/Распределенные вычисления и приложения/DAS_2024_1/emelyanov_artem_lab_7",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "preferences.keymap",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-jdk-9823dce3aa75-28b599e66164-intellij.indexing.shared.core-IU-242.22855.74" />
|
||||
<option value="bundled-js-predefined-d6986cc7102b-5c90d61e3bab-JavaScript-IU-242.22855.74" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="ece3d3db-34eb-42c6-9dfd-726739d49f9f" name="Changes" comment="" />
|
||||
<created>1729344698203</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1729344698203</updated>
|
||||
<workItem from="1729344699237" duration="1655000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="feature: completed lab 7">
|
||||
<option name="closed" value="true" />
|
||||
<created>1729346451585</created>
|
||||
<option name="number" value="00001" />
|
||||
<option name="presentableId" value="LOCAL-00001" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1729346451585</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="2" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<MESSAGE value="feature: completed lab 7" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="feature: completed lab 7" />
|
||||
</component>
|
||||
</project>
|
24
emelyanov_artem_lab_7/Readme.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Балансировка нагрузки в распределённых системах
|
||||
|
||||
>Балансировка нагрузки - распределение сетевых или вычислительных нагрузок между несколькими серверами или ресурсами для оптимизации производительности, надежности и времени отклика.
|
||||
|
||||
Существуют такие **алгоритмы** для балансировки нагрузки, как:
|
||||
|
||||
- **Round Robin** — запросы идут по очереди на серверы.
|
||||
- **Least Connections** — запрос направляется на сервер с наименьшей загрузкой.
|
||||
- **Weighted Round Robin** — учитывает мощность серверов.
|
||||
|
||||
Ну и одними из популярных **технологий**, являются:
|
||||
|
||||
- **NGINX** и **HAProxy** — балансировщики на уровне HTTP/TCP.
|
||||
- **Kubernetes Ingress** — управляет балансировкой в контейнерных средах.
|
||||
|
||||
Также можно осуществить балансировку нагрузки на **базах данных**, с помощью:
|
||||
|
||||
- **Чтение/Запись с репликами** — чтение с реплик, запись в мастер.
|
||||
- **Шардинг** — деление данных по узлам.
|
||||
- **PgBouncer** — управление пулами соединений.
|
||||
|
||||
Для балансировки нагрузки также можно использовать **реверс-прокси**.
|
||||
|
||||
>Реверс-прокси - это сервер, который принимает клиентские запросы и перенаправляет их на соответствующие внутренние сервера. Также он может быть использован для балансировки, кэширования и безопасности.
|
33
emelyanov_artem_lab_8/Readme.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Устройство распределенных систем
|
||||
|
||||
> Сложные системы такие как "ВКонтакте" пишутся в **распределенном** стиле, так как это даёт такие преимущества, как:
|
||||
|
||||
- **Масштабируемость**: Разделение на микросервисы позволяет масштабировать только те части системы, которые испытывают высокую нагрузку, не затрагивая другие компоненты.
|
||||
- **Упрощение разработки и поддержки**: Микросервисная архитектура позволяет выделить разные команды для разработки и поддержки каждого отдельного сервиса, что ускоряет разработку и упрощает управление кодовой базой.
|
||||
- **Устойчивость и отказоустойчивость**: Отказ одного микросервиса не ведет к отказу всей системы, что повышает её надежность.
|
||||
- **Гибкость в выборе технологий**: Разные сервисы могут использовать разные языки программирования, базы данных и фреймворки, которые лучше подходят для конкретной задачи.
|
||||
|
||||
> Но у распределённых систем можно выявить и недостатки, такие как:
|
||||
|
||||
- **Сложность разработки и управления**
|
||||
- **Сложность тестирования**
|
||||
- **Задержки в сетевом взаимодействии**
|
||||
|
||||
> Для управления контейнерами в распределенных системах, были созданы специальные системы автоматизации для управления развертыванием. Называется **орекстратор**, и его преимущества заключаются в:
|
||||
|
||||
- **Автоматическое масштабирование**
|
||||
- **Управление отказами**
|
||||
- **Облегчение развертывания и обновления**
|
||||
- **Управление конфигурациями и секретами**
|
||||
|
||||
> Для асинхронного взаимодействия между сервисами придуманы **очереди сообщений**, такие как, RabbitMQ или Kafka:
|
||||
|
||||
- **Сообщения** — это единицы данных, которые сервисы обмениваются друг с другом.
|
||||
|
||||
**Очереди** обеспечивают:
|
||||
|
||||
- **Буферизацию нагрузки**
|
||||
- **Асинхронность**
|
||||
- **Устойчивость к сбоям**
|
||||
|
||||
> Для внедрения параллельных вычислений в распределенную систему, должны быть весомы поводы, такие как, обработка большого кол-ва данных. Но если задачи простые или важна скорость обмена между сервисами, это может быть уже не так полезно.
|
BIN
kadyrov_aydar_lab_1/Image_1.png
Normal file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
kadyrov_aydar_lab_1/Image_2.png
Normal file
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
BIN
kadyrov_aydar_lab_1/Image_3.png
Normal file
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
kadyrov_aydar_lab_1/Image_4_Work.png
Normal file
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
173
kadyrov_aydar_lab_1/README.md
Normal file
@ -0,0 +1,173 @@
|
||||
# Лабораторная работа №1 - Знакомство с docker и docker-compose
|
||||
|
||||
**Цель**: изучение современных технологий контейнеризации.
|
||||
|
||||
**Задачи**:
|
||||
|
||||
1. Установить средство контейнеризации docker.
|
||||
2. Изучить применение и принципы docker.
|
||||
3. Изучить утилиту docker-compose и структуру файла docker-compose.yml.
|
||||
4. Развернуть не менее 3х различных сервисов при помощи docker-compose.
|
||||
5. Оформить отчёт в формате Markdown и создать Pull Request в git-репозитории.
|
||||
|
||||
## Разворачивание сервисов
|
||||
|
||||
Предлагается развернуть не менее 3х сервисов через docker-compose из списка ниже:
|
||||
|
||||
1. **mediawiki**
|
||||
Движок вики.
|
||||
2. **redmine**
|
||||
Система учёта багов, т.е. баг-трекер.
|
||||
3. **wordpress**
|
||||
Популярная система управления контентом.
|
||||
4. **drupal**
|
||||
Ещё одна популярная система управления контентом.
|
||||
5. **moodle**
|
||||
Система для обучения, на которой построена <https://lms.ulstu.ru>.
|
||||
6. **gitea**
|
||||
Сервис для хранения репозиториев git, на котором работает этот репозиторий.
|
||||
|
||||
Требования и docker-compose:
|
||||
|
||||
* Несколько контейнеров.
|
||||
* Хотя бы один volume.
|
||||
* Хотя бы один порт, проброшенный на хост.
|
||||
|
||||
При этом разворачивание системы должно пройти до конца.
|
||||
Например, должен быть создан администратор и система должна корректно функционировать.
|
||||
Это необходимо будет предоставить в отчёте, поэтому не забывайте делать скриншоты.
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. Установил Docker dekstop на свою ОС (Windows)
|
||||
2. Открыл терминал и с помощью команды "docker --version" проверил корректность установки
|
||||
3. Запустил первый тестовый контейнер с помощью команды "docker run hello-world". (Скрин Image_1)
|
||||
4. Попробовал несколько базовых команд.(Скрин Image_2)
|
||||
5. C помощью команды "docker-compose --version" проверил версию Docker-Compose. (Скрин Image_3)
|
||||
6. Развернул сервисы (Описал ниже)
|
||||
|
||||
## Объяснение работы кода:
|
||||
|
||||
## WordPress:
|
||||
|
||||
1. Эта строка отвечает за образ сборки:
|
||||
|
||||
image: wordpress:latest
|
||||
|
||||
Используем последний официальный образ WordPress.
|
||||
|
||||
2. Задание имени контейнера:
|
||||
|
||||
container_name: wordpress
|
||||
|
||||
Устанавливается имя контейнера как wordpress.
|
||||
|
||||
3. Проброс портов:
|
||||
|
||||
Порт 80 контейнера пробрасывается на порт 8080 хоста.
|
||||
|
||||
4. Тома для хранения данных:
|
||||
|
||||
volumes:
|
||||
- wordpress_data:/var/www/html
|
||||
|
||||
Том wordpress_data монтируется в директорию /var/www/html контейнера для хранения данных WordPress.
|
||||
|
||||
5. Переменные окружения для WordPress:
|
||||
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: example_user
|
||||
WORDPRESS_DB_PASSWORD: example_password
|
||||
WORDPRESS_DB_NAME: example_db
|
||||
|
||||
Здесь задаются параметры для подключения к базе данных:
|
||||
|
||||
WORDPRESS_DB_HOST: адрес базы данных (контейнер с именем db).
|
||||
WORDPRESS_DB_USER: имя пользователя базы данных.
|
||||
WORDPRESS_DB_PASSWORD: пароль пользователя базы данных.
|
||||
WORDPRESS_DB_NAME: имя базы данных.
|
||||
|
||||
6. Зависимость от базы данных:
|
||||
|
||||
depends_on: db
|
||||
|
||||
Указываем, что WordPress зависит от контейнера с базой данных (db), который должен запуститься первым.
|
||||
|
||||
## Redmine:
|
||||
|
||||
1. Эта строка отвечает за образ сборки:
|
||||
|
||||
image: redmine:latest
|
||||
|
||||
Используем последний официальный образ Redmine.
|
||||
|
||||
2. Задание имени контейнера:
|
||||
|
||||
container_name: redmine
|
||||
|
||||
Устанавливается имя контейнера как redmine.
|
||||
|
||||
3. Проброс портов:
|
||||
|
||||
ports:"8081:3000"
|
||||
|
||||
Порт 3000 контейнера пробрасывается на порт 8081 хоста
|
||||
|
||||
4. Переменные окружения для Redmine:
|
||||
|
||||
environment:
|
||||
REDMINE_DB_MYSQL: redmine_db
|
||||
REDMINE_DB_USERNAME: redmine_user
|
||||
REDMINE_DB_PASSWORD: redmine_password
|
||||
|
||||
Задаются параметры для подключения к базе данных:
|
||||
|
||||
REDMINE_DB_MYSQL: имя контейнера базы данных.
|
||||
REDMINE_DB_USERNAME: имя пользователя базы данных.
|
||||
REDMINE_DB_PASSWORD: пароль пользователя базы данных.
|
||||
|
||||
5. Зависимость от базы данных:
|
||||
|
||||
|
||||
depends_on:
|
||||
- redmine_db
|
||||
|
||||
Redmine зависит от контейнера с базой данных redmine_db.
|
||||
|
||||
## Gitea:
|
||||
|
||||
1. Эта строка отвечает за образ сборки:
|
||||
|
||||
image: gitea/gitea:latest
|
||||
|
||||
Используем последний официальный образ Gitea.
|
||||
|
||||
2. Задание имени контейнера:
|
||||
|
||||
container_name: gitea
|
||||
|
||||
Устанавливается имя контейнера как gitea.
|
||||
|
||||
3. Проброс портов:
|
||||
|
||||
ports:"8082:3000"
|
||||
|
||||
Порт 3000 контейнера пробрасывается на порт 8082 хоста
|
||||
|
||||
4. Тома для хранения данных:
|
||||
|
||||
volumes:
|
||||
- gitea_data:/data
|
||||
|
||||
Том gitea_data монтируется в директорию /data контейнера для хранения данных Gitea.
|
||||
|
||||
5. Переменные окружения для Gitea:
|
||||
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
|
||||
## Видео по ссылку:
|
||||
|
||||
https://vk.com/video64471408_456239204?list=ln-0pkqwQsSiTkihNDILH
|
68
kadyrov_aydar_lab_1/docker-compose.yml
Normal file
@ -0,0 +1,68 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
image: wordpress:latest
|
||||
container_name: wordpress
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: example_user
|
||||
WORDPRESS_DB_PASSWORD: example_password
|
||||
WORDPRESS_DB_NAME: example_db
|
||||
volumes:
|
||||
- wordpress_data:/var/www/html
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
db:
|
||||
image: mysql:5.7
|
||||
container_name: wordpress_db
|
||||
environment:
|
||||
MYSQL_DATABASE: example_db
|
||||
MYSQL_USER: example_user
|
||||
MYSQL_PASSWORD: example_password
|
||||
MYSQL_ROOT_PASSWORD: root_password
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
|
||||
redmine:
|
||||
image: redmine:latest
|
||||
container_name: redmine
|
||||
ports:
|
||||
- "8081:3000"
|
||||
environment:
|
||||
REDMINE_DB_MYSQL: redmine_db
|
||||
REDMINE_DB_USERNAME: redmine_user
|
||||
REDMINE_DB_PASSWORD: redmine_password
|
||||
depends_on:
|
||||
- redmine_db
|
||||
|
||||
redmine_db:
|
||||
image: mysql:5.7
|
||||
container_name: redmine_db
|
||||
environment:
|
||||
MYSQL_DATABASE: redmine
|
||||
MYSQL_USER: redmine_user
|
||||
MYSQL_PASSWORD: redmine_password
|
||||
MYSQL_ROOT_PASSWORD: root_password
|
||||
volumes:
|
||||
- redmine_db_data:/var/lib/mysql
|
||||
|
||||
gitea:
|
||||
image: gitea/gitea:latest
|
||||
container_name: gitea
|
||||
ports:
|
||||
- "8082:3000"
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
volumes:
|
||||
- gitea_data:/data
|
||||
|
||||
volumes:
|
||||
wordpress_data:
|
||||
db_data:
|
||||
redmine_db_data:
|
||||
gitea_data:
|
10
kadyrov_aydar_lab_2/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
data/
|
||||
result/
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.env
|
||||
*.venv
|
||||
.env.local
|
||||
venv/
|
||||
env/
|
||||
.idea/
|
78
kadyrov_aydar_lab_2/README.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Лабораторная работа №2 - Разработка простейшего распределённого приложения
|
||||
|
||||
## Задание
|
||||
|
||||
**Цель**: Разработать два приложения, результат работы первого из которых становится входными данными для второго.
|
||||
|
||||
**Задачи**:
|
||||
1. Разработать два приложения:
|
||||
- **app_one**: Ищет в каталоге /var/data файл с наибольшим количеством строк и копирует его содержимое в /var/result/data.txt.
|
||||
- **app_two**: Ищет наименьшее число из файла /var/result/data.txt и сохраняет его в третьей степени в /var/result/result.txt.
|
||||
2. Разработать файлы сборки Docker для каждого приложения.
|
||||
3. Собрать файл docker-compose.yml для запуска обоих приложений.
|
||||
4. Настроить монтирование директорий для обмена данными между контейнерами.
|
||||
5. Правильно закоммитить решение с использованием .gitignore для исключения лишних файлов.
|
||||
|
||||
## Варианты задания:
|
||||
|
||||
1. **app_one**:
|
||||
- Ищет файл с наибольшим количеством строк в каталоге /var/data.
|
||||
- Копирует содержимое этого файла в /var/result/data.txt.
|
||||
|
||||
2. **app_two**:
|
||||
- Читает файл /var/result/data.txt.
|
||||
- Ищет наименьшее число и сохраняет его третью степень в /var/result/result.txt.
|
||||
|
||||
### Требования:
|
||||
1. **Docker**: Платформа для контейнеризации приложений.
|
||||
2. **Docker Compose**: Инструмент для управления многоконтейнерными приложениями на основе файла docker-compose.yml.
|
||||
|
||||
### Сборка и запуск:
|
||||
|
||||
В директории, где находится файл docker-compose.yml, выполним команду для сборки и запуска всех контейнеров:
|
||||
|
||||
|
||||
docker-compose up --build
|
||||
|
||||
|
||||
Эта команда:
|
||||
1. Собирает все Docker-образы для сервисов.
|
||||
2. Запускает контейнеры.
|
||||
3. Автоматически подготавливает данные и выполняет приложения последовательно.
|
||||
|
||||
### Результаты:
|
||||
|
||||
После успешного завершения работы контейнеров можно проверить результаты в папке result:
|
||||
- **data.txt** — файл, полученный после выполнения первого приложения (содержит копию файла с наибольшим количеством строк из папки data).
|
||||
- **result.txt** — файл, полученный после выполнения второго приложения (содержит третью степень наименьшего числа из файла data.txt).
|
||||
|
||||
## Описание работы
|
||||
|
||||
### Программы:
|
||||
|
||||
1. **app_one/main.py**:
|
||||
- Ищет файл с наибольшим количеством строк в каталоге /var/data.
|
||||
- Копирует содержимое этого файла в /var/result/data.txt.
|
||||
|
||||
2. **app_two/main.py**:
|
||||
- Читает файл /var/result/data.txt.
|
||||
- Ищет наименьшее число в файле и возводит его в третью степень.
|
||||
- Сохраняет результат в файл /var/result/result.txt.
|
||||
|
||||
### Генерация данных:
|
||||
|
||||
Для создания случайных данных был написан скрипт generate_data.py:
|
||||
- Создает несколько файлов с целыми числами в каталоге /var/data.
|
||||
- Каждый файл содержит случайные числа, которые будут использоваться первым приложением.
|
||||
|
||||
### Dockerfile:
|
||||
|
||||
Каждое приложение имеет собственный Dockerfile, где указаны шаги для сборки Python-образов и запуска программ.
|
||||
|
||||
## Вывод
|
||||
|
||||
В результате лабораторной работы было создано простейшее распределенное приложение, которое использует Docker и Docker Compose для запуска двух программ, обрабатывающих данные в контейнерах.
|
||||
|
||||
## Видео ВК
|
||||
|
||||
https://vk.com/video64471408_456239205?list=ln-nzLhpgninZdZBOzarw
|
7
kadyrov_aydar_lab_2/app_one/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
CMD ["python", "main.py"]
|
38
kadyrov_aydar_lab_2/app_one/main.py
Normal file
@ -0,0 +1,38 @@
|
||||
import os
|
||||
|
||||
def get_file_with_most_lines(directory):
|
||||
max_lines = 0
|
||||
target_file = ""
|
||||
|
||||
for filename in os.listdir(directory):
|
||||
filepath = os.path.join(directory, filename)
|
||||
if os.path.isfile(filepath):
|
||||
with open(filepath, 'r') as f:
|
||||
line_count = sum(1 for _ in f)
|
||||
if line_count > max_lines:
|
||||
max_lines = line_count
|
||||
target_file = filename
|
||||
|
||||
return target_file
|
||||
|
||||
def copy_file(src_directory, dest_directory, filename):
|
||||
src_filepath = os.path.join(src_directory, filename)
|
||||
dest_filepath = os.path.join(dest_directory, 'data.txt')
|
||||
|
||||
os.makedirs(dest_directory, exist_ok=True)
|
||||
|
||||
with open(src_filepath, 'r') as src_file:
|
||||
with open(dest_filepath, 'w') as dest_file:
|
||||
dest_file.write(src_file.read())
|
||||
|
||||
def main():
|
||||
src_directory = '/var/data'
|
||||
dest_directory = '/var/result'
|
||||
|
||||
target_file = get_file_with_most_lines(src_directory)
|
||||
if target_file:
|
||||
copy_file(src_directory, dest_directory, target_file)
|
||||
print(f"File {target_file} copied to {dest_directory}/data.txt")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
7
kadyrov_aydar_lab_2/app_two/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
CMD ["python", "main.py"]
|
25
kadyrov_aydar_lab_2/app_two/main.py
Normal file
@ -0,0 +1,25 @@
|
||||
import os
|
||||
|
||||
def get_largest_number_from_file(filepath):
|
||||
with open(filepath, 'r') as f:
|
||||
numbers = [int(line.strip()) for line in f.readlines()]
|
||||
return max(numbers)
|
||||
|
||||
def save_square_of_number(number, output_filepath):
|
||||
result = number ** 2
|
||||
with open(output_filepath, 'w') as f:
|
||||
f.write(str(result))
|
||||
|
||||
def main():
|
||||
input_filepath = '/var/result/data.txt'
|
||||
output_filepath = '/var/result/result.txt'
|
||||
|
||||
if os.path.exists(input_filepath):
|
||||
largest_number = get_largest_number_from_file(input_filepath)
|
||||
save_square_of_number(largest_number, output_filepath)
|
||||
print(f"Largest number squared: {largest_number}^2 saved to {output_filepath}")
|
||||
else:
|
||||
print(f"Input file {input_filepath} not found!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
7
kadyrov_aydar_lab_2/data_generator/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
CMD ["python", "generate_data.py"]
|
29
kadyrov_aydar_lab_2/data_generator/generate_data.py
Normal file
@ -0,0 +1,29 @@
|
||||
import os
|
||||
import random
|
||||
|
||||
|
||||
def generate_random_files(directory, num_files, num_lines_per_file, min_value, max_value):
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
|
||||
for i in range(num_files):
|
||||
file_path = os.path.join(directory, f"file_{i + 1}.txt")
|
||||
with open(file_path, 'w') as f:
|
||||
for _ in range(num_lines_per_file):
|
||||
random_number = random.randint(min_value, max_value)
|
||||
f.write(f"{random_number}\n")
|
||||
print(f"Generated file: {file_path}")
|
||||
|
||||
|
||||
def main():
|
||||
data_directory = '/var/data'
|
||||
num_files = 10
|
||||
num_lines_per_file = 12
|
||||
min_value = 1
|
||||
max_value = 100
|
||||
|
||||
generate_random_files(data_directory, num_files, num_lines_per_file, min_value, max_value)
|
||||
print(f"Generated {num_files} files in {data_directory}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
22
kadyrov_aydar_lab_2/docker-compose.yml
Normal file
@ -0,0 +1,22 @@
|
||||
version: '3'
|
||||
services:
|
||||
data_generator:
|
||||
build:
|
||||
context: ./data_generator
|
||||
volumes:
|
||||
- ./data:/var/data
|
||||
app_one:
|
||||
build:
|
||||
context: ./app_one
|
||||
volumes:
|
||||
- ./data:/var/data
|
||||
- ./result:/var/result
|
||||
depends_on:
|
||||
- data_generator
|
||||
app_two:
|
||||
build:
|
||||
context: ./app_two
|
||||
volumes:
|
||||
- ./result:/var/result
|
||||
depends_on:
|
||||
- app_one
|
42
kadyrov_aydar_lab_3/README.md
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
# Лабораторная работа №3 - REST API, шлюз и синхронный обмен данными между микросервисами
|
||||
|
||||
## Задание
|
||||
|
||||
### Цель:
|
||||
Изучение принципов проектирования с использованием паттерна шлюза, организации синхронной передачи данных между микросервисами и применения архитектурного стиля RESTful API.
|
||||
|
||||
### Задачи:
|
||||
1. Создание двух микросервисов, которые реализуют операции CRUD для связанных сущностей.
|
||||
2. Реализация механизма синхронного обмена данными между микросервисами.
|
||||
3. Настройка шлюза на базе Nginx в качестве прозрачного прокси-сервера.
|
||||
|
||||
### Микросервисы:
|
||||
1. **hero_service** — сервис, который управляет информацией о героях.
|
||||
2. **item_service** — сервис, который обрабатывает данные о предметах, принадлежащих героям.
|
||||
|
||||
### Связь между микросервисами:
|
||||
- Один документ (hero) может иметь множество связанных предметов (items) (соотношение 1 ко многим).
|
||||
|
||||
## Как запустить проект:
|
||||
Для запуска приложения необходимо выполнить команду:
|
||||
```bash
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
## Описание работы:
|
||||
|
||||
Для разработки микросервисов был выбран язык программирования Python.
|
||||
|
||||
### Синхронный обмен данными
|
||||
Сервис `hero_service` отправляет HTTP-запросы к `item_service` при выполнении определенных операций CRUD. Это позволяет получать актуальную информацию о предметах, связанных с конкретными героями.
|
||||
|
||||
### Docker Compose
|
||||
Конфигурационный файл `docker-compose.yml` представляет собой многоконтейнерное приложение, которое включает в себя три сервиса: `hero_service`, `item_service` и `nginx`. Функция маршрутизации возложена на сервер Nginx, который обрабатывает запросы и перенаправляет их на соответствующие микросервисы.
|
||||
|
||||
### Nginx
|
||||
Конфигурационный файл Nginx определяет настройки веб-сервера и обратного прокси, который управляет входящими запросами и направляет их на соответствующие сервисы.
|
||||
|
||||
### ВК ВИДЕО
|
||||
|
||||
https://vk.com/video64471408_456239206?list=ln-eG1UX8zXWbZkc651DD
|
26
kadyrov_aydar_lab_3/docker-compose.yml
Normal file
@ -0,0 +1,26 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
hero_service:
|
||||
build:
|
||||
context: ./hero_service
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "5000:5000"
|
||||
|
||||
item_service:
|
||||
build:
|
||||
context: ./item_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:
|
||||
- hero_service
|
||||
- item_service
|
10
kadyrov_aydar_lab_3/hero_service/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
||||
FROM python:3.11
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["python", "main.py"]
|
51
kadyrov_aydar_lab_3/hero_service/main.py
Normal file
@ -0,0 +1,51 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import uuid
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
heroes = {}
|
||||
|
||||
@app.route('/heroes', methods=['GET'])
|
||||
def get_heroes():
|
||||
return jsonify(list(heroes.values()))
|
||||
|
||||
@app.route('/heroes/<uuid:hero_uuid>', methods=['GET'])
|
||||
def get_hero(hero_uuid):
|
||||
hero = heroes.get(str(hero_uuid))
|
||||
if hero:
|
||||
return jsonify(hero)
|
||||
return jsonify({'error': 'Not found'}), 404
|
||||
|
||||
@app.route('/heroes', methods=['POST'])
|
||||
def create_hero():
|
||||
data = request.get_json()
|
||||
hero_uuid = str(uuid.uuid4())
|
||||
hero = {
|
||||
'uuid': hero_uuid,
|
||||
'name': data['name'],
|
||||
'role': data['role'],
|
||||
'strength': data['strength']
|
||||
}
|
||||
heroes[hero_uuid] = hero
|
||||
return jsonify(hero), 201
|
||||
|
||||
@app.route('/heroes/<uuid:hero_uuid>', methods=['PUT'])
|
||||
def update_hero(hero_uuid):
|
||||
hero = heroes.get(str(hero_uuid))
|
||||
if not hero:
|
||||
return jsonify({'error': 'Not found'}), 404
|
||||
data = request.get_json()
|
||||
hero['name'] = data['name']
|
||||
hero['role'] = data['role']
|
||||
hero['strength'] = data['strength']
|
||||
return jsonify(hero)
|
||||
|
||||
@app.route('/heroes/<uuid:hero_uuid>', methods=['DELETE'])
|
||||
def delete_hero(hero_uuid):
|
||||
if str(hero_uuid) in heroes:
|
||||
del heroes[str(hero_uuid)]
|
||||
return '', 204
|
||||
return jsonify({'error': 'Not found'}), 404
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000)
|
1
kadyrov_aydar_lab_3/hero_service/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
Flask
|
10
kadyrov_aydar_lab_3/item_service/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
||||
FROM python:3.11
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["python", "main.py"]
|
51
kadyrov_aydar_lab_3/item_service/main.py
Normal file
@ -0,0 +1,51 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import uuid
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
items = {}
|
||||
|
||||
@app.route('/items', methods=['GET'])
|
||||
def get_items():
|
||||
return jsonify(list(items.values()))
|
||||
|
||||
@app.route('/items/<uuid:item_uuid>', methods=['GET'])
|
||||
def get_item(item_uuid):
|
||||
item = items.get(str(item_uuid))
|
||||
if item:
|
||||
return jsonify(item)
|
||||
return jsonify({'error': 'Not found'}), 404
|
||||
|
||||
@app.route('/items', methods=['POST'])
|
||||
def create_item():
|
||||
data = request.json
|
||||
item_uuid = str(uuid.uuid4())
|
||||
item = {
|
||||
'uuid': item_uuid,
|
||||
'name': data['name'],
|
||||
'type': data['type'],
|
||||
'hero_uuid': data['hero_uuid']
|
||||
}
|
||||
items[item_uuid] = item
|
||||
return jsonify(item), 201
|
||||
|
||||
@app.route('/items/<uuid:item_uuid>', methods=['PUT'])
|
||||
def update_item(item_uuid):
|
||||
item = items.get(str(item_uuid))
|
||||
if not item:
|
||||
return jsonify({'error': 'Not found'}), 404
|
||||
data = request.json
|
||||
item['name'] = data['name']
|
||||
item['type'] = data['type']
|
||||
item['hero_uuid'] = data['hero_uuid']
|
||||
return jsonify(item)
|
||||
|
||||
@app.route('/items/<uuid:item_uuid>', methods=['DELETE'])
|
||||
def delete_item(item_uuid):
|
||||
if str(item_uuid) in items:
|
||||
del items[str(item_uuid)]
|
||||
return '', 204
|
||||
return jsonify({'error': 'Not found'}), 404
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5001)
|
1
kadyrov_aydar_lab_3/item_service/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
Flask
|
11
kadyrov_aydar_lab_3/nginx/nginx.conf
Normal file
@ -0,0 +1,11 @@
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location /heroes {
|
||||
proxy_pass http://hero_service:5000;
|
||||
}
|
||||
|
||||
location /items {
|
||||
proxy_pass http://item_service:5001;
|
||||
}
|
||||
}
|
13
kuzarin_maxim_lab_7/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Балансировка нагрузки в распределённых системах
|
||||
*Балансировка нагрузки в распределенных системах* – это метод распределения сетевого трафика по пулу ресурсов приложения. Для этого существует достаточно большое число инструментов.
|
||||
## Алгоритмы балансировки
|
||||
Всё множество алгоритмов для балансировки можно условно разделить на 2 большие группы: рассчитанные на сохранение состояния и не рассчитанные.
|
||||
- Первые, по большому счёту, балансируют подключения пользователей. Но это никак не гарантирует равномерного распределения их запросов.
|
||||
- Вторые – балансируют запросы между ресурсами. Это позволяет распределять нагрузку чуть более равномерно, но при этом необходимо задумываться о том, что бы система могла работать в режиме без сохранения состояния.
|
||||
|
||||
Сами же алгоритмы, по большому счёту, отличаются только механизмом выбора экземпляра ресурса, к которому перенаправляется запрос. Это может быть равномерное распределение (алгоритм **Round Robin**) или вариации взвешенного (алгоритм **Weighted Round Robin**).
|
||||
На сегодняшний день существует множество открытых технологий балансировки нагрузки. Например, всем известный **Nginx** предоставляет инструменты для настройки подобной функции. Так же есть ряд проектов, полностью заточенных под балансировку нагрузки в Kubernetes (**OpenELB**, **MetalLB**).
|
||||
## Балансировка нагрузки на БД
|
||||
Балансировка нагрузки на БД – более сложный и комплексный вопрос. Он предполагает, что у СУБД должен быть механизм репликации данных, или шардинга. Например, такое умеет **PostgreSQL** и **MongoDB**
|
||||
## Реверс-прокси
|
||||
Реверс-прокси в данном случае напрямую не является инструментом балансировки нагрузки. Скорее это инструмент, эту балансировку и распределённость в целом, скрывающий за единым фасадом. Самым известным представителем этого семейства можно назвать уже упомянутый ранее **Nginx**
|
19
kuzarin_maxim_lab_8/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Устройство распределенных систем
|
||||
Сегодня всё больше и больше сложным систем начинают переводить, или проектировать в распределённом формате.
|
||||
## Причины популярности РС
|
||||
На то есть много причин, перечислим основные из них:
|
||||
|
||||
- **Размеры.** На данный момент заметна тенденция создания не просто отдельных приложений для выполнения конкретных задач, а формирования некоторой экосистемы, где пользователь может делать много всего через единую точку входа. Это повышает сложность проектирования системы: она больше физически не может поместиться в голове у разработчиков. Поэтому переход на структуру, когда всё разбито на мелкие, взаимосвязанные элементы вполне оправдан.
|
||||
|
||||
- **Масштабируемость.** Нагрузка на системы растёт постоянно. И нет оснований полагать, что дальше будет не так. Монолит конечно может держать нагрузку, но он лишён возможности точечного горизонтального масштабирования (упёрлись в то, что у нас очень долго работает механизм отправки сообщений о готовности – увеличили количество таких сервисов – узкое место ушло)
|
||||
|
||||
- **Переиспользуемость.** Написанные один раз сервис авторизации, можно с минимальными доработками перенести и в другие продукты. Это сокращает затраты на разработку системы.
|
||||
|
||||
## Оркестраторы
|
||||
Так же, с приходом распределённых систем важную роль стали играть оркестраторы. Фактически, они смогли собрать в себе сразу ряд полезных функций: мониторинг состояния системы, оповещения пользователей о том, что что-то не так, организация процесса быстрого развёртывания, Возможность сбора, агрегации и анализа логов, для поиска узких мест системы и их дальнейшего устранения. Но, как и всегда, оркестратор имеет и минусы: он требует ресурсы (и порой не малые) для своей работы, порог входа у него не всегда низкий.
|
||||
## Очереди сообщений
|
||||
В распределённой системе, помимо прочего, появляется возможность активно использовать системы асинхронного обмена сообщениями через очереди. Этот механизм значительно экономит ресурсы процессора, так как позволяет вместо нахождения в бесконечном цикле ожидания (пока запрос обработается) делать что-то полезное. К тому же брокеры сообщений, которые часто используются в асинхронной обработке, могут повышать надёжность системы: если в момент обработки сообщения упадёт сервис-приёмник, то информация о подтверждении не доедет до очереди и сообщения будет доставлено через другой экземпляр, а не потеряется, так как очередь будет считать его недоставленным, и повторять отправку.
|
||||
## Параллельные вычисления
|
||||
Что же касается параллельных вычислений – это в первую очередь вопрос производительности. Использование нескольких потоков может ускорить работу кратно, но не всегда это применимо, так как порой перестройка алгоритма может потребовать ресурсов многократно больших, чем будет выигрыш, полученный от ускорения. Если речь о чистой математике(умножить матрицы) – это и правда полезно, а вот попытка развести высокоуровневую бизнес-логику по потокам – может быть изначально плохой идеей.
|
||||
## Недостатки РС
|
||||
Стоит так же отметить, что распределённые системы, несмотря на достоинства, имеют и недостатки. В основном их источник – отсутствие большого опыта создания таких систем, а так же простое правило, что вероятность ошибки в системе тем больше, чем из большего количества элементов она состоит. Отлаживать монолит и искать в нём уязвимости – проще, чем задумываться над тем, что будет, если в момент REST запроса один из сервисов откажет, или сеть упадёт.
|
1
lazarev_andrey_lab_3/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
venv/
|
76
lazarev_andrey_lab_3/README.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Лабораторная работа №2
|
||||
|
||||
## Описание проекта
|
||||
|
||||
Проект разворачивает 3 программы в отдельных контейнерах с использованием Docker Compose:
|
||||
1. **author_service** - сервис, с CRUD операциями для авторов;
|
||||
2. **publication_service** - сервис, с CRUD операциями для публикаций;
|
||||
3. **nginx** - веб-сервер и прокси-сервер, является маршрутизатором.
|
||||
|
||||
Между первыми двумя сервисами имеется связь один(`Автор`) ко многим(`Публикация`).
|
||||
|
||||
## Струкутура проекта
|
||||
|
||||
### Проект состоит из:
|
||||
|
||||
- 2 папки(author_service, publication_service)
|
||||
- Каждая папка содержит в себе файл с расширением `.py` с кодом программы;
|
||||
- Кадлая папка сожержит в себе файл `Dockerfile` с инструкцией по созданию Docker образа.
|
||||
|
||||
- Файл `.gitignore` для исключения временных файлов директории `venv/`;
|
||||
|
||||
- Файл `docker-compose.yml` с конфигурацией Docker Compose;
|
||||
|
||||
- Файл `nginx.conf` конфигурации для веб-сервера NGINX с параметрами работы сервера;
|
||||
|
||||
- Файл `requirements.txt` с перечислением всех необходимых библиотек для запуска.
|
||||
|
||||
Комментарии в файлах.
|
||||
|
||||
## Запуск
|
||||
|
||||
1. Скачать и установить Docker и Docker Compose;
|
||||
2. Перейти в директорию с файлом docker-compose.yml;
|
||||
3. Открыть консоль и запустить сервисы командой
|
||||
```bash
|
||||
docker-compose up --build -d
|
||||
```
|
||||
4. Дождаться запуска всех сервисов
|
||||
```bash
|
||||
[+] Running 3/3
|
||||
✔ Container lazarev_andrey_lab_2-generate-files-1 Started 0.5s
|
||||
✔ Container lazarev_andrey_lab_2-first-1 Started 1.3s
|
||||
✔ Container lazarev_andrey_lab_2-second-1 Started 2.0s
|
||||
```
|
||||
5. Остановка всех сервисов
|
||||
Для завершения работы с сервисами необходимо выполнить команду:
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
Дождаться завершения работы:
|
||||
```bash
|
||||
[+] Running 4/4
|
||||
✔ Container lazarev_andrey_lab_2-second-1 Removed 0.0s
|
||||
✔ Container lazarev_andrey_lab_2-first-1 Removed 0.0s
|
||||
✔ Container lazarev_andrey_lab_2-generate-files-1 Removed 0.0s
|
||||
✔ Network lazarev_andrey_lab_2_default Removed 0.4s
|
||||
```
|
||||
|
||||
## Cписок команд
|
||||
- Author_service
|
||||
- `http://localhost:8000/author_service/author` - список авторов
|
||||
- `http://localhost:8000/author_service/author/{id автора}` - конкретный автор
|
||||
- `http://localhost:8000/author_service/author/full/{id автора}` - автор и полный список его публикаций
|
||||
- `http://localhost:8000/author_service/author?name={имя}&second_name={фамилия}&age={возраст}` - добавление нового автора
|
||||
- `http://localhost:8000/author_service/author/{id автора}?name={новое имя}` - изменение имени автора
|
||||
|
||||
- Publication_service
|
||||
- `http://localhost:8000/publication_service/publication` - список публикаций
|
||||
- `http://localhost:8000/publication_service/publication/{id публикации}` - конкретная публикация
|
||||
- `http://localhost:8000/publication_service/publication/full/{id публикации}` - публикация и полная информация об авторе
|
||||
- `http://localhost:8000/publication_service/publication?name={название}&public_year={год выпуска}&author_id={id автора}` - добавление новой публикации
|
||||
- `http://localhost:8000/publication_service/publication/{id публикации}?name={новое название}` - изменение названия публикации
|
||||
## Видеодемонстрация работоспособности
|
||||
|
||||
[Демонстрация работы сервисов](https://files.ulstu.ru/s/5D2i6gbLn6r2jsA)
|
||||
|
17
lazarev_andrey_lab_3/author_service/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
# Использует базовый образ Python 3.9 на основе slim-версии
|
||||
FROM python:3.10-slim
|
||||
|
||||
# Устанавливаю рабочую директорию внутри контейнера
|
||||
WORKDIR /app
|
||||
|
||||
# Копирую файл requirements.txt в контейнер
|
||||
COPY requirements.txt /app/
|
||||
|
||||
# Устанавливаю зависимости
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
#аналогично копирую
|
||||
COPY author_service/main.py .
|
||||
|
||||
# Задает команду для запуска контейнера
|
||||
CMD ["python", "main.py"]
|
110
lazarev_andrey_lab_3/author_service/main.py
Normal file
@ -0,0 +1,110 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel, Field
|
||||
from uuid import UUID, uuid4
|
||||
import requests
|
||||
|
||||
#Инициализация веб-приложения
|
||||
app = FastAPI(title="Author service")
|
||||
|
||||
#Строка подключения к публикациям
|
||||
publication_url='http://publication_service:8009'
|
||||
|
||||
#Сущность автор с полями имя, фамилия, возраст.
|
||||
class Author(BaseModel):
|
||||
uuid_: UUID = Field(default_factory=uuid4)
|
||||
name: str | None
|
||||
second_name: str | None
|
||||
age: int | None
|
||||
|
||||
#Заранее заполненный список авторов, в некоторых есть uuid они пригодятся при создании публикаций
|
||||
data: list[Author] = [
|
||||
Author(uuid_="92e78b56-0026-4561-b9f6-ba628110c900", name="Андрей", second_name="Лазарев", age=21),
|
||||
Author(uuid_="3203b355-d844-4d5a-ad91-a9e0135cd9d9",name="Павел", second_name="Сорокин", age=20),
|
||||
Author(name="Дарья", second_name="Балберова", age=21),
|
||||
Author(name="Дмитрий", second_name="Курило", age=24),
|
||||
Author(name="Александр", second_name="Дырночкин", age=24)
|
||||
]
|
||||
|
||||
#Получить список всех авторов
|
||||
@app.get("/author", tags=["Author"])
|
||||
def all_authors():
|
||||
return data
|
||||
|
||||
#Получить одного автора по uuid
|
||||
@app.get("/author/{author_id}", tags=["Author"])
|
||||
def get_author(author_id: UUID):
|
||||
author = next((x for x in data if x.uuid_ == author_id), None)
|
||||
|
||||
if not author:
|
||||
return HTTPException(status_code=404, detail="Автор не найден")
|
||||
|
||||
return author
|
||||
|
||||
#Получить одного автора по uuid с списком его публикаций
|
||||
@app.get("/author/full/{author_id}")
|
||||
def get_publications_by_author(author_id: UUID):
|
||||
author = get_author(author_id)
|
||||
|
||||
if not author:
|
||||
return HTTPException(status_code=404, detail="Автор не найден")
|
||||
|
||||
publications = requests.get(f"{publication_url}/publication/author/{author_id}")
|
||||
|
||||
if not publications:
|
||||
return HTTPException(status_code=404, detail="Публикации не найдены")
|
||||
|
||||
result = author.model_dump()
|
||||
result['publications'] = publications.json()
|
||||
|
||||
return result
|
||||
|
||||
#Добавление нового автора, все поля обязательные
|
||||
@app.post("/author", tags=["Author"])
|
||||
def add_author(name: str, second_name: str, age: int):
|
||||
author = next((x for x in data if x.name == name and x.second_name == second_name and x.age == age), None)
|
||||
|
||||
if author:
|
||||
return HTTPException(status_code=404, detail="Такой автор уже существует")
|
||||
|
||||
try:
|
||||
data.append(Author(name=name, second_name=second_name, age=age))
|
||||
return JSONResponse(content={"message": "Автор успешно добавлен"}, status_code=200)
|
||||
|
||||
except Exception as e:
|
||||
return HTTPException(status_code=404, detail={"Автор не был добавлен с ошибкой": str(e)})
|
||||
|
||||
#Изменение автора по uuid
|
||||
@app.put("/author/{author_id}", tags=["Author"])
|
||||
def update_author(author_id: UUID, name: str = None , second_name: str = None, age: int = None):
|
||||
author = get_author(author_id)
|
||||
if author:
|
||||
index = data.index(author)
|
||||
if name:
|
||||
data[index].name = name
|
||||
if second_name:
|
||||
data[index].second_name = second_name
|
||||
if age:
|
||||
data[index].age = age
|
||||
return JSONResponse(content={"message": "Автор успешно изменен"}, status_code=200)
|
||||
|
||||
else:
|
||||
return HTTPException(status_code=404, detail={"Автор не найден": {author}})
|
||||
|
||||
#Удаление автора по uuid
|
||||
@app.delete("/author/{author_id}", tags=["Author"])
|
||||
def delete_author(author_id: UUID):
|
||||
author = get_author(author_id)
|
||||
|
||||
if author:
|
||||
index = data.index(author)
|
||||
del data[index]
|
||||
return JSONResponse(content={"message": "Автор успешно удален"}, status_code=200)
|
||||
|
||||
else:
|
||||
return HTTPException(status_code=404, detail={"Автор не найден": {author}})
|
||||
|
||||
#Запуск
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8008)
|
26
lazarev_andrey_lab_3/docker-compose.yml
Normal file
@ -0,0 +1,26 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
author_service:
|
||||
build:
|
||||
context: . #Контекст сборки — текущая директория (корневая папка проекта).
|
||||
dockerfile: ./author_service/Dockerfile # Путь до Dockerfile для сборки контейнера.
|
||||
expose: # Указывает, какой порт будет открыт внутри контейнера.
|
||||
- 8008
|
||||
|
||||
publication_service:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./publication_service/Dockerfile
|
||||
expose:
|
||||
- 8009
|
||||
|
||||
nginx: # Третий сервис, называемый "nginx".
|
||||
image: nginx # Используется готовый образ NGINX из Docker Hub.
|
||||
ports: # Публикует порты для доступа к NGINX.
|
||||
- 8000:8000 # Проброс порта: внешний порт 8000 связан с внутренним портом 8000.
|
||||
volumes: # Монтирует локальные файлы/директории в контейнер.
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf # Локальный файл nginx.conf будет монтирован в контейнер по пути /etc/nginx/nginx.conf.
|
||||
depends_on: # Зависимости. NGINX будет запускаться после запуска указанных сервисов.
|
||||
- author_service # NGINX зависит от запуска author_service.
|
||||
- publication_service # NGINX зависит от запуска publication_service.
|
27
lazarev_andrey_lab_3/nginx.conf
Normal file
@ -0,0 +1,27 @@
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
server {
|
||||
listen 8000;
|
||||
listen [::]:8000;
|
||||
server_name localhost;
|
||||
|
||||
location /author_service/ {
|
||||
proxy_pass http://author_service:8008/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Prefix $scheme;
|
||||
}
|
||||
|
||||
location /publication_service/ {
|
||||
proxy_pass http://publication_service:8009/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Prefix $scheme;
|
||||
}
|
||||
}
|
||||
}
|
12
lazarev_andrey_lab_3/publication_service/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
#описание в Dockerfile author_service
|
||||
FROM python:3.10-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY publication_service/main.py .
|
||||
|
||||
CMD ["python", "main.py"]
|
113
lazarev_andrey_lab_3/publication_service/main.py
Normal file
@ -0,0 +1,113 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel, Field
|
||||
from uuid import UUID, uuid4
|
||||
import requests
|
||||
|
||||
#Инициализация веб-приложения
|
||||
app = FastAPI(title="Publication service")
|
||||
|
||||
#Строка подключения к авторам
|
||||
author_url='http://author_service:8008'
|
||||
|
||||
#Сущность публикации с полями название, год публикации, ид автора.
|
||||
class Publication(BaseModel):
|
||||
uuid_: UUID = Field(default_factory=uuid4)
|
||||
name: str | None
|
||||
public_year: int | None
|
||||
author_id: UUID | None
|
||||
|
||||
#Заранее заполненный список публикаций, в некоторых есть uuid они пригодятся при создании публикаций
|
||||
data: list[Publication] = [
|
||||
Publication(uuid_="92e78b56-0026-4561-b9f6-ba628110c901", name="книга 1", public_year=2024, author_id="92e78b56-0026-4561-b9f6-ba628110c900"),
|
||||
Publication(name="книга 2", public_year=2022, author_id="92e78b56-0026-4561-b9f6-ba628110c900"),
|
||||
Publication(name="книга 3", public_year=2003, author_id="3203b355-d844-4d5a-ad91-a9e0135cd9d9"),
|
||||
Publication(name="книга 4", public_year=2020, author_id="92e78b56-0026-4561-b9f6-ba628110c900"),
|
||||
Publication(name="книга 5", public_year=2019, author_id="3203b355-d844-4d5a-ad91-a9e0135cd9d9")
|
||||
]
|
||||
|
||||
#Получить список всех публикаций
|
||||
@app.get("/publication", tags=["Publication"])
|
||||
def all_publications():
|
||||
return data
|
||||
|
||||
|
||||
#Получить одной публикации по uuid
|
||||
@app.get("/publication/{publication_id}", tags=["Publication"])
|
||||
def get_publication(publication_id: UUID):
|
||||
publication = next((x for x in data if x.uuid_ == publication_id), None)
|
||||
|
||||
if not publication:
|
||||
return HTTPException(status_code=404, detail="Публикация не найдена")
|
||||
|
||||
return publication
|
||||
|
||||
#Получить одной публикации по uuid с информацие об ее авторе
|
||||
@app.get("/publication/full/{publication_id}")
|
||||
def get_full_publication(publication_id: UUID):
|
||||
publication = get_publication(publication_id)
|
||||
|
||||
if not publication:
|
||||
return HTTPException(status_code=404, detail="Публикаций не найдена")
|
||||
|
||||
author = requests.get(f"{author_url}/author/{publication.author_id}")
|
||||
|
||||
if not author:
|
||||
return HTTPException(status_code=404, detail="Автор не найден")
|
||||
|
||||
result = publication.model_dump()
|
||||
result['author_info'] = author.json()
|
||||
|
||||
return result
|
||||
|
||||
#Добавление новой публикации, все поля обязательные
|
||||
@app.post("/publication", tags=["Publication"])
|
||||
def add_publication(name: str, public_year: int, author_id: UUID):
|
||||
author = next((x for x in data if x.name == name and x.public_year == public_year and x.author_id == author_id), None)
|
||||
|
||||
if author:
|
||||
return HTTPException(status_code=404, detail="Такая публикация уже существует")
|
||||
|
||||
try:
|
||||
data.append(Publication(name=name, public_year=public_year, author_id=author_id))
|
||||
return JSONResponse(content={"message": "Публикация успешно добавлена"}, status_code=200)
|
||||
|
||||
except Exception as e:
|
||||
return HTTPException(status_code=404, detail={"Публикация не была добавлена с ошибкой": str(e)})
|
||||
|
||||
#Изменение публикации по uuid
|
||||
@app.put("/publication/{publication_id}", tags=["Publication"])
|
||||
def update_publication(publication_id: UUID, name: str = None, public_year: int = None, author_id: UUID = None):
|
||||
publication = get_publication(publication_id)
|
||||
|
||||
if publication:
|
||||
index = data.index(publication)
|
||||
if name:
|
||||
data[index].name = name
|
||||
if public_year:
|
||||
data[index].public_year = public_year
|
||||
if author_id:
|
||||
data[index].author_id = author_id
|
||||
return JSONResponse(content={"message": "Публикация успешно изменена"}, status_code=200)
|
||||
|
||||
else:
|
||||
return HTTPException(status_code=404, detail={"Публикация не найдена": {publication}})
|
||||
|
||||
#Удаление публикации по uuid
|
||||
@app.delete("/publication/{publication_id}", tags=["Publication"])
|
||||
def delete_publication(publication_id: UUID):
|
||||
publication = get_publication(publication_id)
|
||||
|
||||
if publication:
|
||||
index = data.index(publication)
|
||||
del data[index]
|
||||
|
||||
return JSONResponse(content={"message": "Публикация успешно удалена"}, status_code=200)
|
||||
|
||||
else:
|
||||
return HTTPException(status_code=404, detail={"Публикация не найдена": {publication}})
|
||||
|
||||
#Запуск
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8009)
|
5
lazarev_andrey_lab_3/requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
fastapi==0.115.0
|
||||
uvicorn==0.31.0
|
||||
pydantic==2.9.2
|
||||
pydantic_core==2.23.4
|
||||
requests==2.32.3
|
5
rogashova_ekaterina_lab_2/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.log
|
||||
.DS_Store
|
1
rogashova_ekaterina_lab_2/data/name.txt
Normal file
@ -0,0 +1 @@
|
||||
1 67 11 45
|
1
rogashova_ekaterina_lab_2/data/namename.txt
Normal file
@ -0,0 +1 @@
|
||||
1 2 3 4 5
|
17
rogashova_ekaterina_lab_2/docker-compose.yml
Normal file
@ -0,0 +1,17 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
findfile:
|
||||
build:
|
||||
context: ./worker-1
|
||||
volumes:
|
||||
- ./data:/var/data
|
||||
- ./result:/var/result
|
||||
|
||||
findnumber:
|
||||
build:
|
||||
context: ./worker-2
|
||||
volumes:
|
||||
- ./result:/var/result
|
||||
depends_on:
|
||||
- findfile
|