Compare commits

...

152 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
NAP
506d544060 kadyrov_aydar_lab_1 2024-10-21 02:36:18 +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
131dc39f6c Merge pull request 'borschevskaya_anna_lab_6 is ready' (#75) from borschevskaya_anna_lab_6 into main
Reviewed-on: #75
2024-10-19 13:08:27 +04:00
d82f47e04c Merge pull request 'emelyanov_artem_lab_5' (#74) from emelyanov_artem_lab_5 into main
Reviewed-on: #74
2024-10-19 12:56:19 +04:00
3175352d02 Merge pull request 'emelyanov_artem_lab_4' (#73) from emelyanov_artem_lab_4 into main
Reviewed-on: #73
2024-10-19 12:49:26 +04:00
2e86e68e12 Merge pull request 'aleikin_artem_lab_1' (#72) from aleikin_artem_lab_1 into main
Reviewed-on: #72
2024-10-19 12:46:13 +04:00
63dd60f20e Merge pull request 'bondarenko_max_lab_1' (#71) from bondarenko_max_lab_1 into main
Reviewed-on: #71
2024-10-19 12:43:27 +04:00
63e031ef17 Merge pull request 'vaksman_valeria_lab_5' (#70) from vaksman_valeria_lab_5 into main
Reviewed-on: #70
2024-10-19 12:33:16 +04:00
5fdabedcd6 Merge pull request 'kuzarin_maxim_lab_6' (#69) from kuzarin_maxim_lab_6 into main
Reviewed-on: #69
2024-10-19 12:30:30 +04:00
9eadb70f85 fix link 2024-10-19 12:28:06 +04:00
5fd241a980 Merge pull request 'dozorova_alena_lab_5' (#67) from dozorova_alena_lab_5 into main
Reviewed-on: #67
2024-10-19 12:26:46 +04:00
4f53dff75f Merge branch 'dozorova_alena_lab_5' of https://git.is.ulstu.ru/Alexey/DAS_2024_1 into dozorova_alena_lab_5 2024-10-19 12:24:25 +04:00
57b7675030 fix link 2024-10-19 12:24:07 +04:00
b1c16dc76c Merge pull request 'bogdanov_dmitry_lab_3' (#68) from bogdanov_dmitry_lab_3 into main
Reviewed-on: #68
2024-10-19 12:23:02 +04:00
309911ed75 Merge pull request 'rogashova_ekaterina_lab_1 is ready' (#66) from rogashova_ekaterina_lab_1 into main
Reviewed-on: #66
2024-10-19 12:08:44 +04:00
d23e808325 Merge pull request 'lazarev_andrey_lab_2' (#65) from lazarev_andrey_lab_2 into main
Reviewed-on: #65
2024-10-19 12:05:03 +04:00
4c974bfb51 Merge pull request 'tsukanova_irina_lab_4' (#64) from tsukanova_irina_lab_4 into main
Reviewed-on: #64
2024-10-19 12:00:40 +04:00
b573569a97 borschevskaya_anna_lab_6 is ready 2024-10-19 10:46:15 +04:00
60c79b64fb feature: deleted lab 5 2024-10-18 17:42:21 +04:00
07105e81a0 feature: completed lab 5 2024-10-18 17:41:49 +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
46b94ea885 feature: completed lab 4 2024-10-18 16:27:06 +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
d3a7046f97 aleikin_artem_lab1 is ready 2024-10-18 00:09:32 +04:00
06d65650ab aleikin_artem_lab1 is ready 2024-10-18 00:05:35 +04:00
992a169c9b Merge branch 'main' into bondarenko_max_lab_1 2024-10-17 23:22:03 +04:00
b82a13c106 bondarenko_max_lab_1 is ready 2024-10-17 23:20:01 +04:00
e33ffef85e fix 2024-10-17 22:43:46 +04:00
9362e62999 йееес lab 5 is ready 2024-10-17 20:01:43 +04:00
430fad9ef4 Merge pull request 'borschevskaya_anna_lab_5 is ready' (#63) from borschevskaya_anna_lab_5 into main
Reviewed-on: #63
2024-10-16 16:50:14 +04:00
d0aedf8495 Merge pull request 'klyushenkova_ksenia_lab_1 is ready' (#62) from klyushenkova_ksenia_lab_1 into main
Reviewed-on: #62
2024-10-16 16:49:29 +04:00
effd849042 Merge pull request 'emelaynov_artem_lab_3' (#61) from emelaynov_artem_lab_3 into main
Reviewed-on: #61
2024-10-16 16:48:48 +04:00
55e18b6a64 Merge pull request 'vaksman_valeria_lab_3' (#60) from vaksman_valeria_lab_3 into main
Reviewed-on: #60
2024-10-16 16:47:28 +04:00
5a7409d60c Merge pull request 'mochalov_danila_lab_2' (#59) from mochalov_danila_lab_2 into main
Reviewed-on: #59
2024-10-16 16:46:55 +04:00
265cf478bf Merge pull request 'tukaeva_alfiya_lab_3 is ready' (#58) from tukaeva_alfiya_lab_3 into main
Reviewed-on: #58
2024-10-16 16:45:44 +04:00
c6f29a13a1 Merge pull request 'vaksman_valeria_lab_4' (#57) from vaksman_valeria_lab_4 into main
Reviewed-on: #57
2024-10-16 16:45:11 +04:00
4103a23984 Merge pull request 'Presnyakova Victoria Lab2' (#56) from presnyakova_victoria_lab_2 into main
Reviewed-on: #56
2024-10-16 16:20:58 +04:00
f8ac151629 Merge pull request 'zhimolostnova_anna_lab 4 complete' (#55) from zhimolostnova_anna_lab_4 into main
Reviewed-on: #55
2024-10-16 15:02:32 +04:00
13b5dfc707 Merge branch 'main' into dozorova_alena_lab_5 2024-10-16 14:31:12 +04:00
5d3517c2b0 Merge pull request 'dozorova_alena_lab_4' (#49) from dozorova_alena_lab_4 into main
Reviewed-on: #49
2024-10-16 14:26:29 +04:00
f3bbfb2efd rogashova_ekaterina_lab_1 is ready 2024-10-14 23:20:57 +04:00
3c6c7f47e8 second lab done 2024-10-14 16:27:36 +04:00
dc7c2c9694 видео 2024-10-14 16:09:46 +04:00
9b4f9b608c все готово, осталось сделать видео 2024-10-14 15:37:29 +04:00
2497e3c742 borschevskaya_anna_lab_5 is ready 2024-10-13 11:03:08 +04:00
Pineapple
a628469960 klyushenkova_ksenia_lab_1 is ready 2024-10-12 23:40:16 +04:00
f107797a2d fix: deleted trash 2024-10-12 16:47:05 +04:00
98e9047b45 feature: completed lab 3 2024-10-12 16:45:56 +04:00
53f96303bc commit for commit 2024-10-11 19:32:26 +04:00
eb7211c6f9 Init. 2024-10-11 19:19:17 +04:00
66ffe827f8 mochalov_danila_lab_2 is ready 2024-10-11 05:18:46 +04:00
a0209b612e tukaeva_alfiya_lab_3 is ready 2024-10-11 01:01:25 +04:00
1f72d4dc70 ох уж этот редми 2024-10-10 21:03:09 +04:00
b351431f51 lab4 now is ready 2024-10-10 21:02:25 +04:00
56baf52b61 lab4 ready 2024-10-10 21:00:15 +04:00
f5ec3f1767 lab2 2024-10-10 18:52:01 +04:00
77790c37fb lab 4 complete 2024-10-09 17:12:11 +03:00
the
735a403027 Добавлен README 2024-10-09 16:38:42 +04:00
the
c67049687b Done 2024-10-09 16:17:26 +04:00
022e2dc49e + 2024-10-08 23:46:12 +04:00
8f24aad349 + 2024-10-08 23:45:44 +04:00
a54e13f7ee + 2024-10-08 23:45:00 +04:00
1bb988ea2f dozorova_alena_lab_6 2024-10-08 23:43:24 +04:00
f7668394b0 dozorova_alena_lab_5 2024-10-08 22:46:32 +04:00
23087c87ea Обновить kuzarin_maxim_lab_6/README.md 2024-09-26 23:14:31 +04:00
5a6580ff8c Фикс README 2024-09-26 22:14:06 +03:00
5f6472b5ff Лаба реализована. Нужно всё проверить в контексте описания 2024-09-26 22:12:33 +03:00
e1950c80ea поправили readme 2024-09-26 22:33:36 +04:00
5586bec4b8 готовая работа 2024-09-26 22:31:01 +04:00
4c74a16753 туториал 3 2024-09-23 22:45:22 +04:00
a830cb2198 туториал 2 2024-09-23 22:42:39 +04:00
9d0fa199f7 first work 2024-09-23 21:35:24 +04:00
f0083bc4cd feature: add .yml and .env files, start readme 2024-09-20 01:42:54 +04:00
2097 changed files with 312965 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,32 @@
# Лабораторная работа 1 - Знакомство с Docker и Docker Compose
## ПИбд-42 || Алейкин Артем
### Описание
В данной лабораторной работе мы разворачиваем три популярных сервиса — MediaWiki и Redmine — с использованием Docker Compose. Каждый сервис работает в своем контейнере и использует общую базу данных PostgreSQL для хранения данных. Мы также настраиваем проброс портов для доступа к веб-интерфейсам сервисов и используем Docker volumes для сохранения данных вне контейнеров.
### Цель проекта
изучение современных технологий контейнеризации
### Шаги для запуска:
1. Клонирование репозитория:
```
git clone <ссылка-на-репозиторий>
cd <папка репозитория>
```
2. Запуск контейнеров:
```
docker-compose up -d
```
3. После запуска должны быть доступны следующие контейнеры:
MediaWiki: http://localhost:8080
Redmine: http://localhost:8081
4. Чтобы остановить контейнеры:
```
docker-compose down
```
Видео демонстрации работы: https://vk.com/video248424990_456239601?list=ln-sCRa9IIiV1VpInn2d1

View File

@ -0,0 +1,45 @@
services:
mediawiki:
image: mediawiki
container_name: mediawiki
ports:
- "8080:80" # Пробрасываем порт 8080 на хост для доступа к MediaWiki
volumes:
- mediawiki_data:/var/www/html/images # Создаем volume для хранения данных MediaWiki
environment:
- MEDIAWIKI_DB_HOST=db
- MEDIAWIKI_DB_NAME=mediawiki
- MEDIAWIKI_DB_USER=root
- MEDIAWIKI_DB_PASSWORD=example
depends_on:
- db
redmine:
image: redmine
container_name: redmine
ports:
- "8081:3000" # Пробрасываем порт 8081 на хост для доступа к Redmine
volumes:
- redmine_data:/usr/src/redmine/files # Создаем volume для хранения данных Redmine
environment:
- REDMINE_DB_POSTGRESQL=db
- REDMINE_DB_DATABASE=redmine
- REDMINE_DB_USERNAME=root
- REDMINE_DB_PASSWORD=example
depends_on:
- db
db:
image: postgres:latest
container_name: db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: example
POSTGRES_DB: postgres
volumes:
- db_data:/var/lib/postgresql # Volume для базы данных
volumes:
mediawiki_data: # volume для MediaWiki
redmine_data: # volume для Redmine
db_data: # volume для базы данных

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

2
bogdanov_dmitry_lab_3/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,22 @@
# Лабораторная работа №3
## Богданов Дмитрий ПИбд-42
### Для выполнения были выбраны следующие сущности:
* Message - содержит uuid (генерируется), text, datetime_sent, user_id
* User - содержит uuid (генерируется), name, surname
Одному пользователю может быть присвоено несколько сообщений.
Соответственно были развернуты 2 сервиса для управления этими сущностями.
### Запуск лабораторной:
Необходимо перейти в папку с файлом compose.yaml и ввести следующую команду:
```
docker-compose up --build -d
```
## Видео с результатом запуска и тестами...
...можно посмотреть по данной [ссылке](https://drive.google.com/file/d/1cJz0z4KduSz1oltmAuieUW7GxxVLNPNo/view).

View File

@ -0,0 +1,27 @@
services:
user_service:
container_name: userService
build:
context: .
dockerfile: ./userService/Dockerfile
expose:
- 20001
message_service:
container_name: messageService
build:
context: .
dockerfile: ./messageService/Dockerfile
expose:
- 20002
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- user_service
- message_service

View File

@ -0,0 +1,11 @@
FROM python:latest
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY messageService/messageService.py .
CMD ["python", "messageService.py"]

View File

@ -0,0 +1,138 @@
from flask import Flask, request, jsonify
from uuid import uuid4
import uuid
import datetime
import requests
class Message:
def __init__(self, text: str, datetime_sent: datetime, uuid_: uuid, user_id: uuid):
if uuid_ is None:
self.uuid_ = uuid4()
else:
self.uuid_ = uuid.UUID(uuid_)
self.text = text
self.datetime_sent = datetime_sent
self.user_id = uuid.UUID(user_id)
def to_dict(self):
return {
'text': self.text,
'datetime_sent': self.datetime_sent,
'user_id': self.user_id,
'uuid': self.uuid_
}
def to_dict_for_users(self):
return {
'title': self.text,
'datetime_sent': self.datetime_sent,
'uuid': self.uuid_
}
def to_dict_with_info(self, user: dict):
return {
'title': self.text,
'datetime_sent': self.datetime_sent,
'user_id': self.user_id,
'user_info': user,
'uuid': self.uuid_
}
messages = [
Message(text='Hi!', datetime_sent=datetime.datetime.now(), uuid_='4add0525-1857-477d-ad35-56790d400b72', user_id='94b171ea-39f6-4a67-9c67-061743f67cfd'),
Message(text='Hello this is a message', datetime_sent=datetime.datetime.now(), uuid_='dd69758d-89e8-49b5-86bf-54ae2adb64e8', user_id='724a3192-70dd-4909-9b0f-c9060a4ab1bd'),
Message(text='Test', datetime_sent=datetime.datetime.now(), uuid_='92389e8d-4365-457e-b37e-78abbc07f194', user_id='94b171ea-39f6-4a67-9c67-061743f67cfd'),
Message(text='Anyone here?', datetime_sent=datetime.datetime.now(), uuid_='f3a1c526-aca2-47e2-afd3-a1c2eac92458', user_id='724a3192-70dd-4909-9b0f-c9060a4ab1bd'),
Message(text='Mambo', datetime_sent=datetime.datetime.now(), uuid_='00abbdb5-e480-4842-bc32-f916894757eb', user_id='46672ea5-3d7b-4137-a0ac-efd898ca4db6')
]
def list_jsonify():
return jsonify([message.to_dict() for message in messages])
app = Flask(__name__)
users_url = 'http://userService:20001/'
@app.route('/', methods=['GET'])
def get_all():
return list_jsonify(), 200
@app.route('/info', methods=['GET'])
def get_all_full():
users: list[dict] = requests.get(users_url).json()
response = []
for message in messages:
for user in users:
if message.user_id == uuid.UUID(user.get('uuid')):
response.append(message.to_dict_with_info(user))
return response, 200
@app.route('/by-user/<uuid:user_uuid>', methods=['GET'])
def get_by_user_id(user_uuid):
return [message.to_dict_for_users() for message in messages if message.user_id == user_uuid], 200
@app.route('/info/<uuid:uuid_>', methods=['GET'])
def get_one_full(uuid_):
for message in messages:
if message.uuid_ == uuid_:
response = requests.get(users_url + str(message.user_id))
return message.to_dict_with_info(response.json()), 200
return f'Сообщение с uuid {uuid_} не найдено', 404
@app.route('/', methods=['POST'])
def create():
data = request.json
text = data.get('text', None)
datetime_sent = datetime.datetime.now()
user_id = data.get('user_id', None)
checking = requests.get(users_url + f'/check/{user_id}')
print(checking)
if checking.status_code == 200:
new_message = Message(text, datetime_sent, None, user_id)
messages.append(new_message)
return get_one(new_message.uuid_)
if checking.status_code == 404:
return f'Пользователь с uuid {user_id} не существует', 404
return 'Неизвестная ошибка', 500
@app.route('/<uuid:uuid_>', methods=['PUT'])
def update_by_id(uuid_):
data = request.json
new_text = data.get('text', None)
for message in messages:
print(message.uuid_)
if message.uuid_ == uuid_:
if new_text is not None:
message.text = new_text
return get_one(message.uuid_)
return f'Сообщение с uuid {uuid_} не найдено', 404
@app.route('/<uuid:uuid_>', methods=['DELETE'])
def delete(uuid_):
for message in messages:
if message.uuid_ == uuid_:
messages.remove(message)
return 'Сообщение успешно удалено', 200
return f'Сообщение с uuid {uuid_} не найдено', 404
@app.route('/<uuid:uuid_>', methods=['GET'])
def get_one(uuid_):
for message in messages:
if message.uuid_ == uuid_:
return message.to_dict(), 200
return f'Сообщение с uuid {uuid_} не найдено', 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=20002, debug=True)

View File

@ -0,0 +1,25 @@
events { worker_connections 1024; }
http {
server {
listen 80;
listen [::]:80;
server_name localhost;
location /userService/ {
proxy_pass http://userService:20001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /messageService/ {
proxy_pass http://messageService:20002/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}

View File

@ -0,0 +1,2 @@
Flask==3.0.3
requests==2.32.3

View File

@ -0,0 +1,11 @@
FROM python:latest
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY userService/userService.py .
CMD ["python", "userService.py"]

View File

@ -0,0 +1,115 @@
from flask import Flask, jsonify, request
from uuid import uuid4
import uuid
import requests
class User:
def __init__(self, name, surname, uuid_: uuid):
if uuid_ is None:
self.uuid_: uuid = uuid4()
else:
self.uuid_: uuid = uuid.UUID(uuid_)
self.name: str = name
self.surname: str = surname
def to_dict(self):
return {
"uuid": self.uuid_,
"name": self.name,
"surname": self.surname
}
def to_dict_with_messages(self, messages: list):
return {
"uuid": self.uuid_,
"name": self.name,
"surname": self.surname,
"messages": messages
}
app = Flask(__name__)
users: list[User] = [
User(name='Dr.', surname='Kino', uuid_='94b171ea-39f6-4a67-9c67-061743f67cfd'),
User(name='Caspian', surname='Holstrom', uuid_='724a3192-70dd-4909-9b0f-c9060a4ab1bd'),
User(name='Admin', surname='Admin', uuid_='46672ea5-3d7b-4137-a0ac-efd898ca4db6')
]
messages_url = 'http://messageService:20002/'
def list_jsonify():
return jsonify([user.to_dict() for user in users])
@app.route('/', methods=['GET'])
def get_all():
return list_jsonify(), 200
@app.route('/<uuid:uuid_>', methods=['GET'])
def get_one(uuid_):
for user in users:
if user.uuid_ == uuid_:
return user.to_dict(), 200
return f'Пользователь с uuid {uuid_} не найден', 404
@app.route('/info/<uuid:uuid_>', methods=['GET'])
def get_one_with_messages(uuid_):
for user in users:
if user.uuid_ == uuid_:
response = requests.get(messages_url + f'by-user/{uuid_}')
print(response.json())
return user.to_dict_with_messages(response.json()), 200
return f'Пользователь с uuid {uuid_} не найден', 404
@app.route('/check/<uuid:uuid_>', methods=['GET'])
def check_exist(uuid_):
for user in users:
if user.uuid_ == uuid_:
return '', 200
return '', 404
@app.route('/', methods=['POST'])
def create():
data = request.json
name = data.get('name', None)
surname = data.get('surname', None)
if name is None or surname is None:
return 'Недостаточно информации для создания пользователя', 404
new_user = User(name, surname, None)
users.append(new_user)
return get_one(new_user.uuid_)
@app.route('/<uuid:uuid_>', methods=['PUT'])
def update_by_id(uuid_):
data = request.json
new_name = data.get('name', None)
new_surname = data.get('surname', None)
for user in users:
if user.uuid_ == uuid_:
if new_name is not None:
user.name = new_name
if new_surname is not None:
user.surname = new_surname
return get_one(user.uuid_)
return f'Пользователь с uuid {uuid_} не найден', 404
@app.route('/<uuid:uuid_>', methods=['DELETE'])
def delete(uuid_):
for user in users:
if user.uuid_ == uuid_:
users.remove(user)
return 'Пользователь удален', 200
return f'Пользователь с uuid {uuid_} не найден', 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=20001, debug=True)

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

20
bondarenko_max_lab_1/.env Normal file
View File

@ -0,0 +1,20 @@
# PostgreSQL конфигурация
POSTGRES_DB=mediawiki
POSTGRES_USER=wikiuser
POSTGRES_PASSWORD=secret
# MediaWiki конфигурация
MEDIAWIKI_DB_NAME=mediawiki
MEDIAWIKI_DB_USER=wikiuser
MEDIAWIKI_DB_PASSWORD=secret
# WordPress конфигурация
WORDPRESS_DB_NAME=wordpress
WORDPRESS_DB_USER=wpuser
WORDPRESS_DB_PASSWORD=secret
# Порты
MEDIAWIKI_PORT=8080
WORDPRESS_PORT=8081
GITEA_WEB_PORT=3000
GITEA_SSH_PORT=222

0
bondarenko_max_lab_1/.gitignore vendored Normal file
View File

View File

@ -0,0 +1,33 @@
# Лабораторная работа 1 - Знакомство с Docker и Docker Compose
### ПИбд-42 || Бондаренко Максим
# Описание работы
> Цель
Изучение современных технологий контейнеризации.
> Задачи
1. Установка Docker Desktop.
2. Принципы Docker.
3. Изучение Docker Compose.
4. Разворачивание сервисов.
5. Оформление отчёта.
> Ход выполнения работы
1. Открыть документацию docker и страницу на habr
2. Скачать и установить docker desktop
3. Смотрим на docker hub как поднять с помощью docker-compose -> gitea, mediawiki, postgresql
4. Запускаем в терминале 'docker-compose up'
4. Исправляем ошибки
5. Записываем видео
6. pushим ветку на git
7. кидаем mrку (merge request)
> Инструкция по запуску
1. Скачать и установить Docker, если ещё не сделано
2. Перейти в bondarenko_max_lab_1 (cd ./bondarenko_max_lab_1/)
3. Выполнить в терминале 'docker-compose up'
4. Проверить порты, на которых docker поднял контейнеры
> Видео демонстрация работы
https://cloud.mail.ru/public/xHc2/JorYr5nDg

View File

@ -0,0 +1,46 @@
# Версия файла docker-compose, которую мы используем
version: '3.8'
# Определение сервисов
# Все последующие готовые образы в services будут взяты с Docker Hub -> image: название:версия
# Далее в services будут использоваться переменные окружения из .env -> ${Переменная_среды}
# Схема пробросов портов -> ports: внешний порт на хосте:внутренний порт в контейнере
services:
db:
image: postgres:latest # Готовый образ postgres
environment:
- POSTGRES_DB=${POSTGRES_DB} # Имя БД
- POSTGRES_USER=${POSTGRES_USER} # Пользователь БД
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # Пароль пользователя БД
volumes:
- db_data:/var/lib/postgresql/data # Монтирование volume для постоянного хранения данных БД
mediawiki:
image: mediawiki:latest # Готовый образ mediawiki
ports:
- "${MEDIAWIKI_PORT}:80" # Проброс порта
volumes:
- mediawiki_data:/var/www/html # Монтирование volume для постоянного хранения данных
environment:
- MEDIAWIKI_DB_HOST=db # Хост БД
- MEDIAWIKI_DB_NAME=${MEDIAWIKI_DB_NAME} # Имя БД для MediaWiki
- MEDIAWIKI_DB_USER=${MEDIAWIKI_DB_USER} # Пользователь БД
- MEDIAWIKI_DB_PASSWORD=${MEDIAWIKI_DB_PASSWORD} # Пароль пользователя БД
gitea:
image: gitea/gitea:latest # Готовый образ gitea
ports:
- "${GITEA_WEB_PORT}:3000" # Проброс веб-порта
- "${GITEA_SSH_PORT}:22" # Проброс SSH-порта
volumes:
- gitea_data:/data # Монтирование volume для постоянного хранения данных
environment:
- USER_UID=1000 # UID пользователя внутри контейнера
- USER_GID=1000 # GID пользователя внутри контейнера
# Определение volumes для хранения данных вне контейнеров
volumes:
mediawiki_data:
wordpress_data:
gitea_data:
db_data:

View File

@ -0,0 +1,30 @@
# Отчет. Лабораторная работа 5
## Описание
В рамках лабораторной работы была реализована программа, которая производит умножение матриц с применением последовательного и паралелльного алгоритма.
При этом последовательный алгоритм достигается с помощью выделения одного потока на выполнение.
При указании одного потока подзадачи по умножению матриц полностью выполняются одним потоком. В качестве подзадачи было
выбрано нахождение строки результирующей матрицы.
По условию задания необходимо было замерить результаты выполнения алгоритмов на квадратных матрицах размерами 100x100,
300x300, 500x500. На всех прогонах можно увидеть, что последовательное выполнение умножения матриц происходит медленнее
в несколько раз медленее. При этом чем больше потоков выделяется для выполнения подзадач, тем быстрее выполняется
алгоритм параллельного умножения.
Результаты представлены на следующих изображениях:
![100](images/100x100.PNG)
![300](images/300x300.PNG)
![500](images/500x500.PNG)
## Как запустить
Необходимо иметь установленную JDK 21. Можно воспользоваться встроенным в нее компилятором (javac), а затем запустить исполняемый файл (java)
или запускать из среды разработки.
При запуске нужно указать аргументы командной строки:
1. размер матриц (integer)
2. режим отладки (boolean) - позволяет выводить в консоль исходные матрицы и промежуточные результаты работы
## Видео-отчет
Работоспособность лабораторной работы можно оценить в следующем [видео](https://disk.yandex.ru/i/ZafQV9CGjBIKIw).
Запуск происходил через IDEA с различными конфигурациями запуска (отличался размер умножаемых матриц и параметр отладки),
чтобы увидеть результаты выполнения на матрицах всех размеров, необходимых по условию задачи.

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.uni.rvip</groupId>
<artifactId>matrix-mul</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@ -0,0 +1,98 @@
package ru.uni.rvip;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
public static void main(String[] args) {
var size = Integer.parseInt(args[0]);
var debugMode = Boolean.parseBoolean(args[1]);
System.out.printf("Размер матриц %dx%d\n", size, size);
var matrix1 = createRandomMatrix(size);
if (debugMode) {
printMatrix(matrix1);
}
var matrix2 = createRandomMatrix(size);
if (debugMode) {
printMatrix(matrix2);
}
var startTime = System.currentTimeMillis();
var result1 = mulMatrix(matrix1, matrix2, 1); // сначала передаем в метод 1 поток-исполнитель
var timeOfExecution = System.currentTimeMillis() - startTime;
if (debugMode) {
printMatrix(result1);
}
System.out.printf("Время умножения матриц с помощью последовательного алгоритма: %d ms\n", timeOfExecution);
var threadCounts = new int[] {2, 4, 6, 8};
for (var threadCount: threadCounts) { // тестирование на разном количестве потоков-исполнителей
startTime = System.currentTimeMillis();
var result2 = mulMatrix(matrix1, matrix2, threadCount);
timeOfExecution = System.currentTimeMillis() - startTime;
if (debugMode) {
printMatrix(result2);
}
System.out.printf("Время умножения матриц с помощью параллельного алгоритма (%d threads): %d ms\n",threadCount, timeOfExecution);
}
}
private static int[][] createRandomMatrix(Integer size) {
var matrix = new int[size][size];
var random = new Random();
for (var i = 0; i < size; i++) {
for (var j = 0; j < size; j++) {
matrix[i][j] = random.nextInt(100);
}
}
return matrix;
}
private static int[][] mulMatrix(int[][] matrix1, int[][] matrix2, Integer threadCount) {
if (matrix1[0].length != matrix2.length) {
throw new IllegalArgumentException("Количество столбцов первой матрицы должна соответствовать количеству строк второй матрицы");
}
var rows = matrix2.length;
var columns = matrix1[0].length;
var result = new int[columns][rows];
try (var executorService = Executors.newFixedThreadPool(threadCount)) {
var futures = new ArrayList<Future<Integer>>();
for (int i = 0; i < rows; i++) {
final int rowI = i;
futures.add(executorService.submit(() -> calculate(rowI, matrix1, matrix2, result)));
}
for (var future : futures) {
future.get();
}
executorService.shutdown();
return result;
} catch (Exception ignored) {
throw new RuntimeException("Ошибка во время выполнения алгоритма");
}
}
private static int calculate(int i, int[][] matrix1, int[][] matrix2, int[][] result) {
for (int j = 0; j < matrix1[0].length; j++) {
result[i][j] = 0;
for (int k = 0; k < matrix2[0].length; k++) {
result[i][j] += matrix1[i][k] * matrix2[k][j];
}
}
return i;
}
private static void printMatrix(int[][] matrix) {
for (int[] ints : matrix) {
for (int elem : ints) {
System.out.printf("%5d\t", elem);
}
System.out.println();
}
}
}

View File

@ -0,0 +1,26 @@
# Отчет. Лабораторная работа 6
## Описание
В рамках лабораторной работы была реализована программа, которая производит вычисление детерминанта матрицы
с применением последовательного и паралелльного алгоритма.
В качестве способа нахождения определителя матрицы был выбран алгоритм нахождения путем разложения по столбцу (строке).
При данном подходе определитель находится как сумма произведений элементов выбранной строки на их алгебраические дополнения.
Была произведена небольшая оптимизация алгоритма - выбор строки для разложения основывался на количестве нулевых элементов в ней.
Чем больше нулей в строке - тем меньше будет выполняться подзадач в алгоритме.
Результаты представлены на следующих изображениях:
![results](images/results.PNG)
Как мы можем увидеть, производительность паралелльной реализации на маленьких матрицах ниже, чем у последовательного подхода,
это связано с дополнительными действиями по созданию и управлению потоками. Однако с увеличением размера матриц мы можем
наблюдать увеличение производительности. Но на больших размерах (от 12х12) матриц алгоритм становится довольно медленным
вне зависимости от применения многопоточности.
## Как запустить
Необходимо иметь установленную JDK 21. Можно воспользоваться встроенным в нее компилятором (javac), а затем запустить исполняемый файл (java)
или запускать из среды разработки.
## Видео-отчет
Работоспособность лабораторной работы можно оценить в следующем [видео](https://disk.yandex.ru/i/-sWDKdW3Q-vbHg).

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.uni.rvip</groupId>
<artifactId>matrix-det</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@ -0,0 +1,155 @@
package ru.uni.rvip;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.IntStream;
public class Main {
private static final ExecutorService executor = Executors.newFixedThreadPool(8);
public static void main(String[] args) {
int size = 12;
for (int i = 5; i <= size; i += 2) {
System.out.printf("Размер матриц %dx%d\n", i, i);
var matrix = createRandomMatrix(i);
var startTime = System.currentTimeMillis();
var result = findDeterminant(matrix);
var timeOfExecution = System.currentTimeMillis() - startTime;
System.out.printf("Детерминант матрицы = %f\n", result);
System.out.printf("Время нахождения детерминанта с помощью последовательного алгоритма: %d ms\n", timeOfExecution);
startTime = System.currentTimeMillis();
findDeterminantParallel(matrix);
timeOfExecution = System.currentTimeMillis() - startTime;
System.out.printf("Время нахождения детерминанта с помощью параллельного алгоритма: %d ms\n", timeOfExecution);
}
executor.shutdown();
}
private static double findDeterminant(double[][] matrix) {
int rows = matrix.length;
int n = matrix[0].length;
if (rows == n && rows == 1) {
return matrix[0][0];
}
if (rows == n && rows == 2) {
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
}
if (rows == n && rows == 3) {
return matrix[0][0] * matrix[1][1] * matrix[2][2] + matrix[0][1] * matrix[1][2] * matrix[2][0] + matrix[0][2] * matrix[1][0] * matrix[2][1]
- (matrix[0][2] * matrix[1][1] * matrix[2][0] + matrix[1][0] * matrix[0][1] * matrix[2][2] + matrix[0][0] * matrix[1][2] * matrix[2][1]);
}
double[][] temporary;
double det = 0;
// поиск наиболее подходящей строки
int indexWithMaxZeros = IntStream.range(0, matrix.length)
.boxed()
.max(Comparator.comparingInt(i ->
(int) Arrays.stream(matrix[i]).filter(x -> x == 0).count()))
.orElse(-1);
double[] maxZerosCountRow = matrix[indexWithMaxZeros];
for (int i = 0; i < n; i++) {
if (maxZerosCountRow[i] > 0) {
temporary = getSubmatrix(matrix, rows, n, i, indexWithMaxZeros);
det += maxZerosCountRow[i] * Math.pow(-1, i) * findDeterminant(temporary);
}
}
return det;
}
private static double findDeterminantParallel(double[][] matrix) {
int rows = matrix.length;
int n = matrix[0].length;
if (rows == n && rows == 1) {
return matrix[0][0];
}
if (rows == n && rows == 2) {
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
}
if (rows == n && rows == 3) {
return matrix[0][0] * matrix[1][1] * matrix[2][2] + matrix[0][1] * matrix[1][2] * matrix[2][0] + matrix[0][2] * matrix[1][0] * matrix[2][1]
- (matrix[0][2] * matrix[1][1] * matrix[2][0] + matrix[1][0] * matrix[0][1] * matrix[2][2] + matrix[0][0] * matrix[1][2] * matrix[2][1]);
}
double det = 0;
// поиск наиболее подходящей строки
int indexWithMaxZeros = IntStream.range(0, matrix.length)
.boxed()
.max(Comparator.comparingInt(i ->
(int) Arrays.stream(matrix[i]).filter(x -> x == 0).count()))
.orElse(-1);
double[] maxZerosCountRow = matrix[indexWithMaxZeros];
var futures = new ArrayList<Future<Double>>();
for (int i = 0; i < n; i++) {
// нахождение подматрицы
final var finalRow = i;
futures.add(executor.submit(() -> {
final var temporary = getSubmatrix(matrix, rows, n, finalRow, indexWithMaxZeros);
return maxZerosCountRow[finalRow] * Math.pow(-1, finalRow) * findDeterminant(temporary);
}));
}
try {
for (var future : futures) {
det += future.get();
}
} catch (InterruptedException | ExecutionException e) {
System.out.println("Возникла ошибка во время многопоточного нахождения определителя матрицы");
return -1;
}
return det;
}
private static double[][] getSubmatrix(double[][] matrix, int rows, int n, int col, int row) {
final var temporary = new double[rows - 1][n - 1];
for (int j = 0; j < rows; j++) {
if (j == row) continue;
for (int k = 0; k < n; k++) {
if (k == col) continue;
//temporary[j][k] = matrix[j][k];
temporary[j < row ? j : j - 1][k < col ? k : k - 1] = matrix[j][k];
}
}
return temporary;
}
private static double[][] createRandomMatrix(Integer size) {
var matrix = new double[size][size];
var random = new Random();
for (var i = 0; i < size; i++) {
for (var j = 0; j < size; j++) {
matrix[i][j] = random.nextInt(50);
}
}
return matrix;
}
private static void printMatrix(double[][] matrix) {
for (double[] ints : matrix) {
for (double elem : ints) {
System.out.printf("%5f\t", elem);
}
System.out.println();
}
}
}

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
### Популярность распределенных систем (РС)
Распределенные системы обладают рядом преимуществ, которые побуждают разработчиков адаптировать уже существующие решения к такой архитектуре (и переписывать монолит на микросервисы).
Данный подход позволяет сделать систему более отказоустойчивой, так как её компоненты становятся независимыми друг от друга.
Особенно это важно для сложных систем, для которых появляется возможность масштабировать отдельные узлы РС.
Также при разработке распределенных систем удобно распределяются задачи между командами разработки и есть возможность
использовать свой стек технологий для каждого сервиса.
### Системы оркестрации
Системы оркестрации автоматизируют развертывание, масштабирование и управление контейнерами, что значительно упрощает
разработку и сопровождение. Однако вместе с тем они накладывают определенные требования к участникам команды разработки,
которым необходимо иметь хотя бы базовые знания об используемой технологии.
### Очереди сообщений
Очереди обработки сообщений позволяют организовать асинхронное взаимодействие между компонентами системы.
Такой подход важен, если процесс при отправке сообщения не должен блокироваться, а ответ необязательно должен быть
получен и обработан мгновенно. Сообщения, которые передаются по очередям, могут быть некоторыми событиями или командами,
на которые могут отреагировать сразу несколько сервисов, подписанных на очередь.
### Преимущества и недостатки РС
К преимуществам РС, как уже было сказано выше, можно отнести масштабируемость, отказоустойчивость, гибкость внедрения новых функциональностей,
а к недостаткам - сложности отладки возникающих проблем и тестирования,
возникающие сетевые задержки при прохождении запроса через несколько сервисов.
### Параллелизм: за и против
Параллельные вычисления нужно применять там, где это действительно нужно. Например, при обработке больших данных, при возможности
разбиения задачи на несколько независимых подзадач.
Однако в случаях, где важна последовательность операций,
параллелизм может привести к усложнению логики приложения и ошибкам, а при малом объеме данных для обработки параллелизм может только ухудшить производительность.

View File

@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
</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}") = "ConsumerDelay", "ConsumerDelay.csproj", "{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E7AED20-0868-42FE-9C39-581BC9D2BB22}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,22 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["ConsumerDelay.csproj", "."]
RUN dotnet restore "./ConsumerDelay.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./ConsumerDelay.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./ConsumerDelay.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ConsumerDelay.dll"]

View File

@ -0,0 +1,24 @@
using ConsumerDelay;
var rabbitHost = Environment.GetEnvironmentVariable("RABBIT_HOST") ?? "localhost";
var rabbitUsername = Environment.GetEnvironmentVariable("RABBIT_USERNAME") ?? "user";
var rabbitPassword = Environment.GetEnvironmentVariable("RABBIT_PASSWORD") ?? "password";
var rabbitExchange = Environment.GetEnvironmentVariable("RABBIT_EXCHANGE") ?? "ReportIn";
var rabbitQueue = Environment.GetEnvironmentVariable("RABBIT_QUEUE") ?? "Second";
Thread.Sleep(2000);
var receiver = new Receiver(rabbitHost, rabbitUsername, rabbitPassword);
receiver.SubscribeTo(rabbitExchange, (message) =>
{
var rnd = new Random();
Console.WriteLine($"Пришло сообщение: {message}");
Thread.Sleep(rnd.Next(2000, 3000));
Console.WriteLine($"Обработка сообщения завершена");
},
rabbitQueue);
while (true) ;

View File

@ -0,0 +1,82 @@
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
namespace ConsumerDelay
{
public class Receiver : IDisposable
{
private readonly ConnectionFactory _connectionFactory;
private readonly IConnection _connection;
private readonly IModel _channel;
public Dictionary<string, HashSet<string>> Queues { get; private set; } = new();
public Receiver(string brockerHost, string brockerUsername, string brockerPassword)
{
_connectionFactory = new ConnectionFactory() { HostName = brockerHost, UserName = brockerUsername, Password = brockerPassword };
_connection = _connectionFactory.CreateConnection();
_channel = _connection.CreateModel();
}
public bool SubscribeTo(string exchange, Action<string> handler, string? queueName = null)
{
try
{
if (!Queues.ContainsKey(exchange))
{
_channel.ExchangeDeclare(exchange: "logs", type: ExchangeType.Fanout);
Queues.Add(exchange, new HashSet<string>());
}
if (queueName != null)
_channel.QueueDeclare(queue: queueName,
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
queueName = queueName ?? _channel.QueueDeclare().QueueName;
_channel.QueueBind(queue: queueName,
exchange: exchange,
routingKey: string.Empty);
var consumer = new EventingBasicConsumer(_channel);
consumer.Received += (model, ea) =>
{
try
{
var message = Encoding.UTF8.GetString(ea.Body.ToArray());
handler(message);
_channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
};
_channel.BasicConsume(queue: queueName,
autoAck: false,
consumer: consumer);
Queues[exchange].Add(queueName);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return false;
}
~Receiver() => Dispose();
public void Dispose()
{
_connection.Dispose();
_channel.Dispose();
}
}
}

View File

@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
</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}") = "ConsumerSimple", "ConsumerSimple.csproj", "{ACA8DE52-E29E-41BA-B3DA-213AF316685E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ACA8DE52-E29E-41BA-B3DA-213AF316685E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACA8DE52-E29E-41BA-B3DA-213AF316685E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACA8DE52-E29E-41BA-B3DA-213AF316685E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACA8DE52-E29E-41BA-B3DA-213AF316685E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {73265D6C-436C-470E-AE8A-17047E6C2ECC}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,22 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["ConsumerSimple.csproj", "."]
RUN dotnet restore "./ConsumerSimple.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./ConsumerSimple.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./ConsumerSimple.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ConsumerSimple.dll"]

View File

@ -0,0 +1,23 @@
using ConsumerSimple;
var rabbitHost = Environment.GetEnvironmentVariable("RABBIT_HOST") ?? "localhost";
var rabbitUsername = Environment.GetEnvironmentVariable("RABBIT_USERNAME") ?? "user";
var rabbitPassword = Environment.GetEnvironmentVariable("RABBIT_PASSWORD") ?? "password";
var rabbitExchange = Environment.GetEnvironmentVariable("RABBIT_EXCHANGE") ?? "ReportIn";
var rabbitQueue = Environment.GetEnvironmentVariable("RABBIT_QUEUE") ?? "First";
Thread.Sleep(2000);
var receiver = new Receiver(rabbitHost, rabbitUsername, rabbitPassword);
receiver.SubscribeTo(rabbitExchange, (message) =>
{
var rnd = new Random();
Console.WriteLine($"Пришло сообщение: {message}");
Console.WriteLine($"Сообщение обрабатывается мгновенно");
},
rabbitQueue);
while (true) ;

View File

@ -0,0 +1,10 @@
{
"profiles": {
"ConsumerSimple": {
"commandName": "Project"
},
"Container (Dockerfile)": {
"commandName": "Docker"
}
}
}

View File

@ -0,0 +1,82 @@
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
namespace ConsumerSimple
{
public class Receiver : IDisposable
{
private readonly ConnectionFactory _connectionFactory;
private readonly IConnection _connection;
private readonly IModel _channel;
public Dictionary<string, HashSet<string>> Queues { get; private set; } = new();
public Receiver(string brockerHost, string brockerUsername, string brockerPassword)
{
_connectionFactory = new ConnectionFactory() { HostName = brockerHost, UserName = brockerUsername, Password = brockerPassword };
_connection = _connectionFactory.CreateConnection();
_channel = _connection.CreateModel();
}
public bool SubscribeTo(string exchange, Action<string> handler, string? queueName = null)
{
try
{
if (!Queues.ContainsKey(exchange))
{
_channel.ExchangeDeclare(exchange: "logs", type: ExchangeType.Fanout);
Queues.Add(exchange, new HashSet<string>());
}
if (queueName != null)
_channel.QueueDeclare(queue: queueName,
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
queueName = queueName ?? _channel.QueueDeclare().QueueName;
_channel.QueueBind(queue: queueName,
exchange: exchange,
routingKey: string.Empty);
var consumer = new EventingBasicConsumer(_channel);
consumer.Received += (model, ea) =>
{
try
{
var message = Encoding.UTF8.GetString(ea.Body.ToArray());
handler(message);
_channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
};
_channel.BasicConsume(queue: queueName,
autoAck: false,
consumer: consumer);
Queues[exchange].Add(queueName);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return false;
}
~Receiver() => Dispose();
public void Dispose()
{
_connection.Dispose();
_channel.Dispose();
}
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>EmitLog_</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,24 @@
using System.Text;
using RabbitMQ.Client;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.ExchangeDeclare(exchange: "logs", type: ExchangeType.Fanout);
var message = GetMessage(args);
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "logs",
routingKey: string.Empty,
basicProperties: null,
body: body);
Console.WriteLine($" [x] Sent {message}");
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
static string GetMessage(string[] args)
{
return ((args.Length > 0) ? string.Join(" ", args) : "info: Hello World!");
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,32 @@
using System.Text;
using RabbitMQ.Client;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.QueueDeclare(queue: "task_queue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
var message = GetMessage(args);
var body = Encoding.UTF8.GetBytes(message);
var properties = channel.CreateBasicProperties();
properties.Persistent = true;
channel.BasicPublish(exchange: string.Empty,
routingKey: "task_queue",
basicProperties: properties,
body: body);
Console.WriteLine($" [x] Sent {message}");
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
static string GetMessage(string[] args)
{
return ((args.Length > 0) ? string.Join(" ", args) : "Hello World!");
}

View File

@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@ -0,0 +1,20 @@
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Publisher.csproj", "."]
RUN dotnet restore "./Publisher.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./Publisher.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./Publisher.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Publisher.dll"]

View File

@ -0,0 +1,40 @@
using Publisher;
using System.Text;
var rabbitHost = Environment.GetEnvironmentVariable("RABBIT_HOST") ?? "localhost";
var rabbitUsername = Environment.GetEnvironmentVariable("RABBIT_USERNAME") ?? "user";
var rabbitPassword = Environment.GetEnvironmentVariable("RABBIT_PASSWORD") ?? "password";
var rabbitExchange = Environment.GetEnvironmentVariable("RABBIT_EXCHANGE") ?? "ReportIn";
var sender = new Sender(rabbitHost, rabbitUsername, rabbitPassword);
sender.AddExcange(rabbitExchange);
var rnd = new Random();
while (true)
{
StringBuilder sb = new();
var type = rnd.Next();
switch (type%2)
{
case 0:
{
sb.Append($"Был запрошен отчет о данных под номером {rnd.Next(1000)}");
break;
}
case 1:
{
sb.Append($"Был запрошен отчет об ошибках под номером {rnd.Next(1000)}");
break;
}
}
var text = sb.ToString();
Console.WriteLine($"Было опубликовано сообщение: {text}");
sender.PublishToExchange(rabbitExchange, text);
await Task.Delay(1000);
}

View File

@ -0,0 +1,10 @@
{
"profiles": {
"Publisher": {
"commandName": "Project"
},
"Container (Dockerfile)": {
"commandName": "Docker"
}
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
</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}") = "Publisher", "Publisher.csproj", "{C23890FA-A4DD-4E5B-897F-37210C2F60CE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C23890FA-A4DD-4E5B-897F-37210C2F60CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C23890FA-A4DD-4E5B-897F-37210C2F60CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C23890FA-A4DD-4E5B-897F-37210C2F60CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C23890FA-A4DD-4E5B-897F-37210C2F60CE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3BB20EB3-DE49-46CE-8C7A-D956E3DE90BC}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,69 @@
using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Publisher
{
public class Sender : IDisposable
{
private readonly ConnectionFactory _connectionFactory;
private readonly IConnection _connection;
private readonly IModel _channel;
public HashSet<string> Exchanges { get; private set; } = new HashSet<string>();
public Sender(string brockerHost, string brockerUsername, string brockerPassword)
{
_connectionFactory = new ConnectionFactory() { HostName = brockerHost, UserName = brockerUsername, Password = brockerPassword };
_connection = _connectionFactory.CreateConnection();
_channel = _connection.CreateModel();
}
public bool AddExcange(string exchange, string exchangeType = ExchangeType.Fanout)
{
try
{
_channel.ExchangeDeclare(exchange, exchangeType);
Exchanges.Add(exchange);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return false;
}
public bool PublishToExchange(string exchange, string message)
{
try
{
if (!Exchanges.Contains(exchange))
return false;
var messageBody = Encoding.UTF8.GetBytes(message);
_channel.BasicPublish(exchange: exchange,
routingKey: string.Empty,
basicProperties: null,
body: messageBody);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return false;
}
~Sender() => Dispose();
public void Dispose()
{
_connection.Dispose();
_channel.Dispose();
}
}
}

View File

@ -0,0 +1,39 @@
# Лабораторная работа 4
В рамках данной работы были реализованы несколько проектов, работающих с RabbitMQ.
## tutorial
Для каждого урока были созданы консольные проекты
### "Hello World!"
![Task 1](image-4.png)
### Work Queues
![Task 2](image-5.png)
### Publish/Subscribe
![Task 3](image-6.png)
## Описание
В качестве предметной области была выбрана система запросов отчета двух видов: отчета и ошибок.
## Запуск
Для запуска лабораторной работы необходимо иметь запущенный Docker на устройстве.
Необходимо перейти в папку, где располагается данный файл. Далее открыть терминал и ввести команду:
```
docker compose up -d --build
```
Порты для RabbitMQ были 8081 (для UI) и 5672.
## Анализ
Первоначальный вариант запуска предполагает, что имеется всего 2 потребителя:
1. Тратит на обработку сообщения 2-3 секунды
2. Тратит на обработку сообщения крайне малое время
Задержка при обработке понижает пропускную способность обработчика, что вызывает переполнение очереди. Это подтверждается скринами.
<br/>
![alt text](image.png)
<br/>
![alt text](image-1.png)
<br/>
Теперь запустим несколько обычных обработчиков. Очередь не переполнена постоянно, а периодически, соответственно обработчики вполне справляются с потоком сообщений и увеличение их количества позволит в принципе избавиться от переполнения
<br/>
![alt text](image-2.png)
<br/>
![alt text](image-3.png)
## Видеодемонстрация
Демонстрация: (https://drive.google.com/file/d/16gJMGbMKSFZ_I5gCzuDekpAqUrhbpFRA/view?usp=sharing) Стоит обратить внимание на то, что настройки docker compose файла не гарантируют верный порядок подъема контейнеров, из-за чего некоторые контейнеры пришлось перезапустить.

View File

@ -0,0 +1,29 @@
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
Console.WriteLine(" [*] Waiting for messages.");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($" [x] Received {message}");
};
channel.BasicConsume(queue: "hello",
autoAck: true,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,31 @@
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.ExchangeDeclare(exchange: "logs", type: ExchangeType.Fanout);
// declare a server-named queue
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName,
exchange: "logs",
routingKey: string.Empty);
Console.WriteLine(" [*] Waiting for logs.");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
byte[] body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($" [x] {message}");
};
channel.BasicConsume(queue: queueName,
autoAck: true,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
</ItemGroup>
</Project>

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