Compare commits

...

86 Commits

Author SHA1 Message Date
3b9698ac38 Merge pull request 'tsukanova_irina_lab_5' (#108) from tsukanova_irina_lab_5 into main
Reviewed-on: #108
2024-10-26 13:01:34 +04:00
a456344432 Merge pull request 'rogashova_ekaterina_lab_3' (#106) from rogashova_ekaterina_lab_3 into main
Reviewed-on: #106
2024-10-26 13:00:05 +04:00
383a5e3b25 Merge pull request 'kadyrov_aydar_lab_3' (#105) from kadyrov_aydar_lab_3 into main
Reviewed-on: #105
2024-10-26 12:59:18 +04:00
2834efbbce Merge pull request 'kadyrov_aydar_lab_2' (#104) from kadyrov_aydar_lab_2 into main
Reviewed-on: #104
2024-10-26 12:58:55 +04:00
decc46b37c Merge pull request 'tukaeva_alfiya_lab_5' (#103) from tukaeva_alfiya_lab_5 into main
Reviewed-on: #103
2024-10-26 12:58:23 +04:00
a41e76795f Merge pull request 'artamonova_tatyana_lab_2 is ready' (#102) from artamonova_tatyana_lab_2 into main
Reviewed-on: #102
2024-10-26 12:57:40 +04:00
bcfec37329 Merge pull request 'bogdanov_dmitry_lab_5' (#101) from bogdanov_dmitry_lab_5 into main
Reviewed-on: #101
2024-10-26 12:56:47 +04:00
e17b0b0d61 Merge pull request 'bogdanov_dmitry_lab_4' (#100) from bogdanov_dmitry_lab_4 into main
Reviewed-on: #100
2024-10-26 12:56:28 +04:00
62290fc43d Merge pull request 'zhimolostnova_anna_lab_6' (#95) from zhimolostnova_anna_lab_6 into main
Reviewed-on: #95
2024-10-26 12:56:04 +04:00
0b5fb8da2e Merge pull request 'zhimolostnova lab 5 complete' (#94) from zhimolostnova_anna_lab_5 into main
Reviewed-on: #94
2024-10-26 12:53:57 +04:00
9c6ef7e89e Merge pull request 'vaksman_valeria_lab_6' (#91) from vaksman_valeria_lab_6 into main
Reviewed-on: #91
2024-10-26 12:52:19 +04:00
e763cf36e2 Merge pull request 'yakovleva_yulia_lab_4 is ready' (#90) from yakovleva_yulia_lab_4 into main
Reviewed-on: #90
2024-10-26 12:51:56 +04:00
adf3f384a3 Merge pull request 'dozorova_alena_lab_8' (#99) from dozorova_alena_lab_8 into main
Reviewed-on: #99
2024-10-26 12:50:30 +04:00
5ae6cd3cf1 Merge pull request 'dozorova_alena_lab_7' (#98) from dozorova_alena_lab_7 into main
Reviewed-on: #98
2024-10-26 12:42:04 +04:00
daf3742ce6 Merge pull request 'zhimolostnova lab 8 complete' (#97) from zhimolostnova_anna_lab_8 into main
Reviewed-on: #97
2024-10-26 12:35:58 +04:00
fb37a53f66 Merge pull request 'zhimolostnova lab 7 complete' (#96) from zhimolostnova_anna_lab_7 into main
Reviewed-on: #96
2024-10-26 12:35:18 +04:00
23e035f9b2 Merge pull request 'vaksman_valeria_lab_8' (#93) from vaksman_valeria_lab_8 into main
Reviewed-on: #93
2024-10-26 12:31:26 +04:00
556d8cf262 Merge pull request 'vaksman_valeria_lab_7' (#92) from vaksman_valeria_lab_7 into main
Reviewed-on: #92
2024-10-26 12:30:20 +04:00
419790f5df Merge pull request 'borschevskaya_anna_lab_8' (#89) from borschevskaya_anna_lab_8 into main
Reviewed-on: #89
2024-10-26 12:27:12 +04:00
54a9b8a778 Merge pull request 'kadyrov_aydar_lab_1' (#88) from kadyrov_aydar_lab_1 into main
Reviewed-on: #88
2024-10-26 12:23:44 +04:00
3aeae245fa Merge pull request 'lazarev_andrey_lab_3' (#87) from lazarev_andrey_lab_3 into main
Reviewed-on: #87
2024-10-26 12:23:21 +04:00
382273ccb8 Merge pull request 'borschevskaya_anna_lab_7 is ready' (#86) from borschevskaya_anna_lab_7 into main
Reviewed-on: #86
2024-10-26 12:20:06 +04:00
4a37f55328 Merge pull request 'rogashova_ekaterina_lab_2' (#85) from rogashova_ekaterina_lab_2 into main
Reviewed-on: #85
2024-10-26 12:14:14 +04:00
4e32398903 Merge pull request 'artamonova_tatyana_lab_1' (#84) from artamonova_tatyana_lab_1 into main
Reviewed-on: #84
2024-10-26 12:13:43 +04:00
e69819aedd Merge pull request 'tukaeva_alfiya_lab_4 is ready' (#83) from tukaeva_alfiya_lab_4 into main
Reviewed-on: #83
2024-10-26 12:12:59 +04:00
d9c4402ec9 Merge pull request 'kuzarin_maxim_lab_8' (#81) from kuzarin_maxim_lab_8 into main
Reviewed-on: #81
2024-10-26 12:07:16 +04:00
93687ad850 Merge pull request 'kuzarin_maxim_lab_7' (#80) from kuzarin_maxim_lab_7 into main
Reviewed-on: #80
2024-10-26 11:37:45 +04:00
4528bcd22c Merge pull request 'emelyanov_artem_lab_8' (#79) from emelyanov_artem_lab_8 into main
Reviewed-on: #79
2024-10-26 11:35:53 +04:00
eef1d03249 Merge pull request 'emelyanov_artem_lab_7' (#78) from emelyanov_artem_lab_7 into main
Reviewed-on: #78
2024-10-26 11:34:21 +04:00
7e09109cd2 Merge pull request 'emelyanov_artem_lab_6' (#77) from emelyanov_artem_lab_6 into main
Reviewed-on: #77
2024-10-26 11:33:34 +04:00
f46724e5cf Merge pull request 'dozorova_alena_lab_6' (#76) from dozorova_alena_lab_6 into main
Reviewed-on: #76
2024-10-26 11:27:22 +04:00
72b0b63e58 видео 2024-10-25 21:03:05 +04:00
fd54e426b5 осталось видео 2024-10-25 20:52:46 +04:00
ad8894c0ca сделано, осталось дописать ридми 2024-10-25 16:56:56 +04:00
edea94a4f2 Готово 2024-10-25 14:10:33 +04:00
NAP
5700e75965 kadyrov_aydar_lab_3 2024-10-25 01:10:46 +04:00
NAP
9e9711f004 kadyrov_aydar_lab_2 2024-10-24 20:07:47 +04:00
014845df45 tukaeva_alfiya_lab_5 is ready 2024-10-24 15:46:08 +04:00
636592bbac artamonova_tatyana_lab_2 is ready 2024-10-23 21:29:01 +04:00
the
6711e8b0f6 Lab5 2024-10-23 18:36:31 +04:00
the
c91aa6e1f3 Исправление 2024-10-23 15:58:26 +04:00
the
d340d34c0b README, исправления, изображения 2024-10-23 15:56:48 +04:00
the
aaff3b8183 Lab4 2024-10-23 14:11:04 +04:00
06a7114499 lab 8 complete 2024-10-22 20:52:00 +03:00
0246f32bcf lab 7 complete 2024-10-22 20:19:09 +03:00
417368d25e fix readme 2024-10-22 19:31:03 +03:00
20a39fa9a5 lab 6 complete 2024-10-22 19:30:00 +03:00
fb15f87160 lab 5 complete 2024-10-22 18:23:30 +03:00
f86dfba785 lab8 wow 2024-10-21 21:21:28 +04:00
e874c69b62 lab7 is ready 2024-10-21 21:17:41 +04:00
6f0726185a lab six is ready yep 2024-10-21 21:10:24 +04:00
JulYakJul
b4b0ef7730 fix readme 2024-10-21 14:35:40 +04:00
JulYakJul
4d51941016 fix readme 2024-10-21 14:34:02 +04:00
JulYakJul
a07b272c79 yakovleva_yulia_lab_4 is ready 2024-10-21 14:31:58 +04:00
7cb94c14b0 borschevskaya_anna_lab_8 is ready 2024-10-21 08:52:52 +04:00
1ef9e02d32 lazarev_andrey_lab_3 is ready 2024-10-20 23:32:58 +04:00
ff8a87ebb8 borschevskaya_anna_lab_7 is ready 2024-10-20 22:37:56 +04:00
740d49d368 Готово 2024-10-20 21:59:10 +04:00
df1b8bd8ce artamonova_tatyana_lab_1 is ready 2024-10-20 19:29:17 +04:00
7549429b6b artamonova_tatyana_lab_1 is ready 2024-10-20 19:25:21 +04:00
00d9e2409a tukaeva_alfiya_lab_4 is ready 2024-10-20 19:04:32 +04:00
098cb9b9ad Обновить kuzarin_maxim_lab_8/README.md
маленькая стилистическая доработка
2024-10-19 19:44:12 +04:00
af39fdc505 Текст написан, нужно проверить отображение Md 2024-10-19 18:42:32 +03:00
ef603a8056 Обновить kuzarin_maxim_lab_7/README.md
Небольшая проблема с двум \n
2024-10-19 19:36:31 +04:00
c8b3124074 Добавлено эссе в виде MD файла 2024-10-19 18:34:37 +03:00
ce853de348 feature: completed lab 8 2024-10-19 18:40:59 +04:00
c3ac60eaa2 fix: delete .idea 2024-10-19 18:03:24 +04:00
e12438b727 feature: completed lab 7 2024-10-19 18:00:48 +04:00
aa54f9187f написали эссе 2024-10-19 14:47:04 +04:00
b1d8660774 + 2024-10-19 14:18:21 +04:00
6c66654acc feature: deleted lab 6 2024-10-19 14:17:40 +04:00
1d9c308bb4 правим в принципе разметку 2024-10-19 14:17:25 +04:00
a64b6c7329 feature: completed lab 6 2024-10-19 14:17:22 +04:00
7ec5c45faa попытка выравнивания 2024-10-19 14:06:09 +04:00
340dc6aa19 добавляем эссе 2024-10-19 14:04:46 +04:00
a152275bb7 Merge branch 'main' into dozorova_alena_lab_6 2024-10-19 13:17:33 +04:00
6e3ec51fe7 Merge branch 'main' into dozorova_alena_lab_6 2024-10-19 13:17:09 +04:00
9eadb70f85 fix link 2024-10-19 12:28:06 +04:00
JulYakJul
0ebd562be2 Merge branch 'main' into yakovleva_yulia_lab_4 2024-10-18 17:02:27 +04:00
JulYakJul
22a3917d28 work 3 done 2024-10-18 16:59:19 +04:00
JulYakJul
94b8ba783c work 2.2 done 2024-10-18 16:24:18 +04:00
JulYakJul
060bd2321e work 2 done 2024-10-18 16:11:22 +04:00
JulYakJul
a8f1b39dd7 work 1 done 2024-10-18 15:42:10 +04:00
022e2dc49e + 2024-10-08 23:46:12 +04:00
a54e13f7ee + 2024-10-08 23:45:00 +04:00
1bb988ea2f dozorova_alena_lab_6 2024-10-08 23:43:24 +04:00
191 changed files with 7069 additions and 0 deletions

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
main.py

View 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>

View 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
View 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>

View 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. Она позволяет разработчикам легко развертывать и управлять приложениями в изолированной среде.

View 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
View File

@ -0,0 +1,5 @@
*.pyc
__pycache__
*.egg-info
*.dist-info
.DS_Store

View 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

View 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:

View File

@ -0,0 +1,3 @@
15
18
18

View File

@ -0,0 +1 @@
2

View 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"]

View 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()

View 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"]

View 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()

View 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)

View 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()

View 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()

View 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()

View 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)

View 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()

View 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()

View 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()

View 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()

View 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()

View 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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View 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)

View 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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View 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, в котором с помощью настройки конфигурационного файла можно указать как настройки проксирования,
так и добавить логику балансировки.

View File

@ -0,0 +1,30 @@
# Отчет. Лабораторная работа 8
### Популярность распределенных систем (РС)
Распределенные системы обладают рядом преимуществ, которые побуждают разработчиков адаптировать уже существующие решения к такой архитектуре (и переписывать монолит на микросервисы).
Данный подход позволяет сделать систему более отказоустойчивой, так как её компоненты становятся независимыми друг от друга.
Особенно это важно для сложных систем, для которых появляется возможность масштабировать отдельные узлы РС.
Также при разработке распределенных систем удобно распределяются задачи между командами разработки и есть возможность
использовать свой стек технологий для каждого сервиса.
### Системы оркестрации
Системы оркестрации автоматизируют развертывание, масштабирование и управление контейнерами, что значительно упрощает
разработку и сопровождение. Однако вместе с тем они накладывают определенные требования к участникам команды разработки,
которым необходимо иметь хотя бы базовые знания об используемой технологии.
### Очереди сообщений
Очереди обработки сообщений позволяют организовать асинхронное взаимодействие между компонентами системы.
Такой подход важен, если процесс при отправке сообщения не должен блокироваться, а ответ необязательно должен быть
получен и обработан мгновенно. Сообщения, которые передаются по очередям, могут быть некоторыми событиями или командами,
на которые могут отреагировать сразу несколько сервисов, подписанных на очередь.
### Преимущества и недостатки РС
К преимуществам РС, как уже было сказано выше, можно отнести масштабируемость, отказоустойчивость, гибкость внедрения новых функциональностей,
а к недостаткам - сложности отладки возникающих проблем и тестирования,
возникающие сетевые задержки при прохождении запроса через несколько сервисов.
### Параллелизм: за и против
Параллельные вычисления нужно применять там, где это действительно нужно. Например, при обработке больших данных, при возможности
разбиения задачи на несколько независимых подзадач.
Однако в случаях, где важна последовательность операций,
параллелизм может привести к усложнению логики приложения и ошибкам, а при малом объеме данных для обработки параллелизм может только ухудшить производительность.

12
dozorova_alena_lab_6/.gitignore vendored Normal file
View 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

View 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>

View 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

View 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;
}
}
}

View 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;
}
}

View 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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,17 @@
**Балансировка нагрузки** предполагает равномерную нагрузку вычислительных узлов (процессора многопроцессорной ЭВМ или компьютера в сети)
Следует различать _статическую_ и _динамическую_ балансировки, где первая выполняется до начала выполнения распределенного приложения, а вторая в процессе.
Приведем примеры алгоритмов балансировки:
* *Круговой алгоритм*: - распределение входящих запросов между несколькими серверами в порядке циклической очередности. Модификации:
- *Взвешенный циклический перебор*, учитывающий мощность серверов
- *Динамический круговой алгоритм*, который учитывает текущую нагрузку на серверы при распределении запросов.
* *Наименьшее количество соединений* - направление входящих запросов на сервер с наименьшим количеством активных соединений в данный момент времени. Модификации:
- *Взвешенное наименьшее количество соединений* направляет запросы на сервер с наименьшим соотношением активных соединений к его назначенному весу.
* *Наименьшее время отклика* - направление запросов на сервер, который демонстрируют наилучшую производительность в данный момент. Он учитывает два ключевых фактора: время отклика сервера и количество активных соединений.
* *Наименьший объем трафика* динамический алгоритм балансировки нагрузки, который направляет входящие запросы на сервер, передающий наименьший объем данных в текущий момент.
Существует достаточно много различных технологий как для программной, так и аппаратной балансировки. Наиболее популярные решения это Nginx и OpenELB, MetalLB для Kubernetes.
Для обеспечения балансировка нагрузки на базах данных используется механизм **репликации**. Балансировка нагрузки заключается в распределении запросов от пользователей по разным копиям (репликам) базы данных.
**Реверс-прокси** — это сервер, который находится перед веб-серверами и пересылает запросы от клиента на эти веб-серверы. Он не является в полной мере балансировщиком, но может выполнять его функцию при большой нагрузке на систему.

View File

@ -0,0 +1,12 @@
Распределенные системы все больше набирают популярность в наше время. Этому способствуют такие ее качества как:
* *Устойчивость и масштабируемость* возможность репликации позволяет поддерживать в работоспособном состоянии всю систему при отказе одного из ее компонентов
* *Принцип единой ответственности* реализация каждого элемента системы в виде сервиса, ответственного за что-то одно, упрощает поддержку и дальнейшее развитие системы.
* *Увеличение производительности* использование одновременно несколько распределенных сервисов позволяет повысить скорость обработки данных
**Оркестраторы** призваны обеспечивать мониторинг за состоянием системы и выполнять функции по ее оптимизации: организация быстрого развертывания, балансировка нагрузки. Оркестраторы так же позволяют уведомлять пользователей о возникших проблемах и выполнять сбор, агрегацию и анализ логов. Подобная обработка данных позволяет выявить узкие места системы. Но оркестратор так же требует ресурсы на собственную работу.
Под **сообщениями** подразумеваются данные, которыми обмениваются сервисы внутри системы. **Очереди сообщений** обеспечивают асинхронный обмен информацией. Сервис, отправляющий сообщение, взаимодействует не с другим сервисом, а с очередью и ждет ответа, не блокируя собственные процессы. Брокеры сообщений повышают отказоустойчивость такого обмена они гарантируют доставку сообщения до сервиса-приемника.
На мой взгляд основными *преимуществами* распределенных приложений являются их масштабируемость и отказоустойчивость. Но в то же время они имеют и достаточно весомые *недостатки* сложность их поддержки и реализации, а также сложность обработки и трассировки транзакций.
**Параллельные вычисления** в подобных системах следует внедрять только в том случае, когда *выигрыш от скорости вычислений значительно повышает затраты на выделение ресурсов*. Вычислить математические характеристики матрицы, к примеру, может быть оправдано, а вот разнесение высокоуровневой логики может быть неоправданно

View 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("--------------------------------------------------");
}
}
}

View 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;
}
}

View 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;
}
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View 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>

View File

@ -0,0 +1,24 @@
# Балансировка нагрузки в распределённых системах
>Балансировка нагрузки - распределение сетевых или вычислительных нагрузок между несколькими серверами или ресурсами для оптимизации производительности, надежности и времени отклика.
Существуют такие **алгоритмы** для балансировки нагрузки, как:
- **Round Robin** — запросы идут по очереди на серверы.
- **Least Connections** — запрос направляется на сервер с наименьшей загрузкой.
- **Weighted Round Robin** — учитывает мощность серверов.
Ну и одними из популярных **технологий**, являются:
- **NGINX** и **HAProxy** — балансировщики на уровне HTTP/TCP.
- **Kubernetes Ingress** — управляет балансировкой в контейнерных средах.
Также можно осуществить балансировку нагрузки на **базах данных**, с помощью:
- **Чтение/Запись с репликами** — чтение с реплик, запись в мастер.
- **Шардинг** — деление данных по узлам.
- **PgBouncer** — управление пулами соединений.
Для балансировки нагрузки также можно использовать **реверс-прокси**.
>Реверс-прокси - это сервер, который принимает клиентские запросы и перенаправляет их на соответствующие внутренние сервера. Также он может быть использован для балансировки, кэширования и безопасности.

View File

@ -0,0 +1,33 @@
# Устройство распределенных систем
> Сложные системы такие как "ВКонтакте" пишутся в **распределенном** стиле, так как это даёт такие преимущества, как:
- **Масштабируемость**: Разделение на микросервисы позволяет масштабировать только те части системы, которые испытывают высокую нагрузку, не затрагивая другие компоненты.
- **Упрощение разработки и поддержки**: Микросервисная архитектура позволяет выделить разные команды для разработки и поддержки каждого отдельного сервиса, что ускоряет разработку и упрощает управление кодовой базой.
- **Устойчивость и отказоустойчивость**: Отказ одного микросервиса не ведет к отказу всей системы, что повышает её надежность.
- **Гибкость в выборе технологий**: Разные сервисы могут использовать разные языки программирования, базы данных и фреймворки, которые лучше подходят для конкретной задачи.
> Но у распределённых систем можно выявить и недостатки, такие как:
- **Сложность разработки и управления**
- **Сложность тестирования**
- **Задержки в сетевом взаимодействии**
> Для управления контейнерами в распределенных системах, были созданы специальные системы автоматизации для управления развертыванием. Называется **орекстратор**, и его преимущества заключаются в:
- **Автоматическое масштабирование**
- **Управление отказами**
- **Облегчение развертывания и обновления**
- **Управление конфигурациями и секретами**
> Для асинхронного взаимодействия между сервисами придуманы **очереди сообщений**, такие как, RabbitMQ или Kafka:
- **Сообщения** — это единицы данных, которые сервисы обмениваются друг с другом.
**Очереди** обеспечивают:
- **Буферизацию нагрузки**
- **Асинхронность**
- **Устойчивость к сбоям**
> Для внедрения параллельных вычислений в распределенную систему, должны быть весомы поводы, такие как, обработка большого кол-ва данных. Но если задачи простые или важна скорость обмена между сервисами, это может быть уже не так полезно.

10
kadyrov_aydar_lab_2/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
data/
result/
__pycache__/
*.py[cod]
*.env
*.venv
.env.local
venv/
env/
.idea/

View 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

View File

@ -0,0 +1,7 @@
FROM python:3.9-slim
WORKDIR /app
COPY . /app
CMD ["python", "main.py"]

View 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()

View File

@ -0,0 +1,7 @@
FROM python:3.9-slim
WORKDIR /app
COPY . /app
CMD ["python", "main.py"]

View 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()

View File

@ -0,0 +1,7 @@
FROM python:3.9-slim
WORKDIR /app
COPY . /app
CMD ["python", "generate_data.py"]

View 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()

View 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

View 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

View 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

View 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"]

View 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)

View File

@ -0,0 +1 @@
Flask

View 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"]

View 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)

View File

@ -0,0 +1 @@
Flask

View File

@ -0,0 +1,11 @@
server {
listen 80;
location /heroes {
proxy_pass http://hero_service:5000;
}
location /items {
proxy_pass http://item_service:5001;
}
}

View File

@ -0,0 +1,13 @@
# Балансировка нагрузки в распределённых системах
*Балансировка нагрузки в распределенных системах* это метод распределения сетевого трафика по пулу ресурсов приложения. Для этого существует достаточно большое число инструментов.
## Алгоритмы балансировки
Всё множество алгоритмов для балансировки можно условно разделить на 2 большие группы: рассчитанные на сохранение состояния и не рассчитанные.
- Первые, по большому счёту, балансируют подключения пользователей. Но это никак не гарантирует равномерного распределения их запросов.
- Вторые балансируют запросы между ресурсами. Это позволяет распределять нагрузку чуть более равномерно, но при этом необходимо задумываться о том, что бы система могла работать в режиме без сохранения состояния.
Сами же алгоритмы, по большому счёту, отличаются только механизмом выбора экземпляра ресурса, к которому перенаправляется запрос. Это может быть равномерное распределение (алгоритм **Round Robin**) или вариации взвешенного (алгоритм **Weighted Round Robin**).
На сегодняшний день существует множество открытых технологий балансировки нагрузки. Например, всем известный **Nginx** предоставляет инструменты для настройки подобной функции. Так же есть ряд проектов, полностью заточенных под балансировку нагрузки в Kubernetes (**OpenELB**, **MetalLB**).
## Балансировка нагрузки на БД
Балансировка нагрузки на БД более сложный и комплексный вопрос. Он предполагает, что у СУБД должен быть механизм репликации данных, или шардинга. Например, такое умеет **PostgreSQL** и **MongoDB**
## Реверс-прокси
Реверс-прокси в данном случае напрямую не является инструментом балансировки нагрузки. Скорее это инструмент, эту балансировку и распределённость в целом, скрывающий за единым фасадом. Самым известным представителем этого семейства можно назвать уже упомянутый ранее **Nginx**

View File

@ -0,0 +1,19 @@
# Устройство распределенных систем
Сегодня всё больше и больше сложным систем начинают переводить, или проектировать в распределённом формате.
## Причины популярности РС
На то есть много причин, перечислим основные из них:
- **Размеры.** На данный момент заметна тенденция создания не просто отдельных приложений для выполнения конкретных задач, а формирования некоторой экосистемы, где пользователь может делать много всего через единую точку входа. Это повышает сложность проектирования системы: она больше физически не может поместиться в голове у разработчиков. Поэтому переход на структуру, когда всё разбито на мелкие, взаимосвязанные элементы вполне оправдан.
- **Масштабируемость.** Нагрузка на системы растёт постоянно. И нет оснований полагать, что дальше будет не так. Монолит конечно может держать нагрузку, но он лишён возможности точечного горизонтального масштабирования (упёрлись в то, что у нас очень долго работает механизм отправки сообщений о готовности увеличили количество таких сервисов узкое место ушло)
- **Переиспользуемость.** Написанные один раз сервис авторизации, можно с минимальными доработками перенести и в другие продукты. Это сокращает затраты на разработку системы.
## Оркестраторы
Так же, с приходом распределённых систем важную роль стали играть оркестраторы. Фактически, они смогли собрать в себе сразу ряд полезных функций: мониторинг состояния системы, оповещения пользователей о том, что что-то не так, организация процесса быстрого развёртывания, Возможность сбора, агрегации и анализа логов, для поиска узких мест системы и их дальнейшего устранения. Но, как и всегда, оркестратор имеет и минусы: он требует ресурсы (и порой не малые) для своей работы, порог входа у него не всегда низкий.
## Очереди сообщений
В распределённой системе, помимо прочего, появляется возможность активно использовать системы асинхронного обмена сообщениями через очереди. Этот механизм значительно экономит ресурсы процессора, так как позволяет вместо нахождения в бесконечном цикле ожидания (пока запрос обработается) делать что-то полезное. К тому же брокеры сообщений, которые часто используются в асинхронной обработке, могут повышать надёжность системы: если в момент обработки сообщения упадёт сервис-приёмник, то информация о подтверждении не доедет до очереди и сообщения будет доставлено через другой экземпляр, а не потеряется, так как очередь будет считать его недоставленным, и повторять отправку.
## Параллельные вычисления
Что же касается параллельных вычислений это в первую очередь вопрос производительности. Использование нескольких потоков может ускорить работу кратно, но не всегда это применимо, так как порой перестройка алгоритма может потребовать ресурсов многократно больших, чем будет выигрыш, полученный от ускорения. Если речь о чистой математике(умножить матрицы) это и правда полезно, а вот попытка развести высокоуровневую бизнес-логику по потокам может быть изначально плохой идеей.
## Недостатки РС
Стоит так же отметить, что распределённые системы, несмотря на достоинства, имеют и недостатки. В основном их источник отсутствие большого опыта создания таких систем, а так же простое правило, что вероятность ошибки в системе тем больше, чем из большего количества элементов она состоит. Отлаживать монолит и искать в нём уязвимости проще, чем задумываться над тем, что будет, если в момент REST запроса один из сервисов откажет, или сеть упадёт.

1
lazarev_andrey_lab_3/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
venv/

View 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)

View 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"]

View 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)

View 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.

View 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;
}
}
}

View 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"]

View 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)

View 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
View File

@ -0,0 +1,5 @@
__pycache__/
*.pyc
*.pyo
*.log
.DS_Store

View File

@ -0,0 +1 @@
1 67 11 45

View File

@ -0,0 +1 @@
1 2 3 4 5

View 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

View File

@ -0,0 +1,30 @@
# Лабораторная работа №2
#### Вариант 1 сервиса: 5. Ищет в каталоге /var/data файл с самым коротким названием и перекладывает его в /var/result/data.txt.
#### Вариант 2 сервиса: 1. Ищет набольшее число из файла /var/data/data.txt и сохраняет его вторую степень в /var/result/result.txt.
## Ход работы:
1. Создаем необходимые директории с файлами - программами на языке Python, которые выполняют необходимые действия по варианту.
Программа 1:
Принимает каталог в качестве входных данных и находит файл с самым коротким названием в этом катологе. Затем перемещает его в другой каталог.
Программа 2:
Принимает на вход файл с целыми числами, находит наибольшее и возводит его в квадрат. Результат выводит в новый файл.
2. Создаем файл docker-compose.yml.
Здесь происходит настройка зависимостей между сервисами, гарантируя, что 2 программа запустится только после 1. Он также настраивает монтирование папок, чтобы программы имели доступ к каталогам и файлам.
3. Также в папку с каждой программой необходимо добавить Dockerfile. Dockerfile — это текстовый файл, который содержит инструкции по сборке образа Docker. Он используется для создания настраиваемых образов Docker, которые могут запускать ваши приложения в изолированной и воспроизводимой среде.
## Как запустить это?
Для запуска данной конфигурации Docker Compose выполните следующие шаги:
1. Открыть терминал и перейти в директорию, где находится docker-compose.yml.
2. Выполнить команду
Выполнить команду:
```
docker compose up --build
```
3. Дождаться, пока Docker Compose запустит все контейнеры.
## Видео
Работоспособность представлена на [видео](https://vk.com/video204968285_456240925).

View File

@ -0,0 +1,11 @@
# Используем базовый образ с Python
FROM python:3.9-slim
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем файл с кодом программы в контейнер
COPY findfile.py .
# Запускаем приложение
CMD ["python", "findfile.py"]

View File

@ -0,0 +1,23 @@
import os
import shutil
def find_shortest_file(directory):
shortest_file = None
shortest_file_length = float('inf')
for file in os.listdir(directory):
file_path = os.path.join(directory, file)
if os.path.isfile(file_path) and len(file) < shortest_file_length:
shortest_file = file_path
shortest_file_length = len(file)
return shortest_file
def move_file(source, destination):
shutil.move(source, destination)
directory = "/var/data"
shortest_file = find_shortest_file(directory)
if shortest_file is not None:
destination = "/var/result/data.txt"
move_file(shortest_file, destination)

View File

@ -0,0 +1,11 @@
# Используем базовый образ с Python
FROM python:3.9-slim
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем файл с кодом программы в контейнер
COPY findnumber.py .
# Запускаем приложение
CMD ["python", "findnumber.py"]

View File

@ -0,0 +1,29 @@
import os
def find_max_number(file_path):
max_number = float('-inf')
with open(file_path, "r") as f:
# Читаем все строки из файла
content = f.read()
# Разделяем строки по пробелам для получения списка чисел
numbers = map(int, content.split())
for number in numbers:
if number > max_number:
max_number = number
return max_number
def save_result(result, file_path):
with open(file_path, "w") as f:
f.write(str(result))
def print_result(result):
print("Квадрат наибольшего числа:", result)
file_path = "/var/result/data.txt"
max_number = find_max_number(file_path)
result_file_path = "/var/result/result.txt"
save_result(max_number ** 2, result_file_path)
print_result(max_number ** 2)

2
rogashova_ekaterina_lab_3/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.venv
/.idea

Some files were not shown because too many files have changed in this diff Show More