Compare commits
No commits in common. "main" and "tukaeva_alfiya_lab_3" have entirely different histories.
main
...
tukaeva_al
3
dozorova_alena_lab_6/.gitignore → .gitignore
vendored
@ -7,6 +7,3 @@
|
|||||||
/dozorova_alena_lab_2/ConsoleApp2/.vs
|
/dozorova_alena_lab_2/ConsoleApp2/.vs
|
||||||
/dozorova_alena_lab_2/ConsoleApp2/bin
|
/dozorova_alena_lab_2/ConsoleApp2/bin
|
||||||
/dozorova_alena_lab_2/ConsoleApp2/obj
|
/dozorova_alena_lab_2/ConsoleApp2/obj
|
||||||
/dozorova_alena_lab_6/ConsoleApp1/.vs
|
|
||||||
/dozorova_alena_lab_6/ConsoleApp1/bin
|
|
||||||
/dozorova_alena_lab_6/ConsoleApp1/obj
|
|
@ -1 +0,0 @@
|
|||||||
main.py
|
|
@ -1,12 +0,0 @@
|
|||||||
<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>
|
|
@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
@ -1,4 +0,0 @@
|
|||||||
<?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>
|
|
@ -1,32 +0,0 @@
|
|||||||
# Лабораторная работа 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
|
|
@ -1,45 +0,0 @@
|
|||||||
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 для базы данных
|
|
@ -1,48 +0,0 @@
|
|||||||
## Отчет по 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. Она позволяет разработчикам легко развертывать и управлять приложениями в изолированной среде.
|
|
@ -1,61 +0,0 @@
|
|||||||
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
@ -1,5 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
__pycache__
|
|
||||||
*.egg-info
|
|
||||||
*.dist-info
|
|
||||||
.DS_Store
|
|
@ -1,22 +0,0 @@
|
|||||||
## Лабораторная работа №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
|
|
@ -1,22 +0,0 @@
|
|||||||
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:
|
|
@ -1,3 +0,0 @@
|
|||||||
15
|
|
||||||
18
|
|
||||||
18
|
|
@ -1 +0,0 @@
|
|||||||
2
|
|
@ -1,14 +0,0 @@
|
|||||||
# Используем образ 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"]
|
|
@ -1,21 +0,0 @@
|
|||||||
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()
|
|
@ -1,14 +0,0 @@
|
|||||||
# Используем образ 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"]
|
|
@ -1,26 +0,0 @@
|
|||||||
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_2/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
data/
|
|
||||||
result/
|
|
@ -1,41 +0,0 @@
|
|||||||
# Лабораторная работа №2
|
|
||||||
|
|
||||||
## Богданов Дмитрий ПИбд-42
|
|
||||||
|
|
||||||
### Для выполнения была проделана следующая работа:
|
|
||||||
Были написаны и развернуты 3 сервиса: генератор файлов, 2 приложения для работы с этими файлами по вариантам 2 и 1 соответственно:
|
|
||||||
|
|
||||||
Вариант 2 (для первого приложения):
|
|
||||||
|
|
||||||
```Формирует файл /var/result/data.txt из первых строк всех файлов каталога /var/data.```
|
|
||||||
|
|
||||||
Вариант 1 (для второго приложения):
|
|
||||||
|
|
||||||
```Ищет набольшее число из файла /var/data/data.txt и сохраняет его вторую степень в /var/result/result.txt.```
|
|
||||||
|
|
||||||
Приложения работают совместно, используя общий монтированный том для записи и получения информации:
|
|
||||||
```
|
|
||||||
volumes:
|
|
||||||
- ./data:/var/data
|
|
||||||
```
|
|
||||||
```
|
|
||||||
volumes:
|
|
||||||
- ./data:/var/data
|
|
||||||
- ./result:/var/result
|
|
||||||
```
|
|
||||||
```
|
|
||||||
volumes:
|
|
||||||
- ./result:/var/result
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Запуск лабораторной:
|
|
||||||
Необходимо перейти в папку с файлом docker-compose.yaml и ввести следующую команду:
|
|
||||||
```
|
|
||||||
docker compose up --build
|
|
||||||
```
|
|
||||||
Сервис генератора сгенерирует папки data и result, где будут сгенерированы входные файлы и файл-результат их обработки соответственно.
|
|
||||||
|
|
||||||
## Видео с результатом запуска:
|
|
||||||
|
|
||||||
Видео-демонстрацию работы можно посмотреть по данной [ссылке](https://drive.google.com/file/d/1CmVZjJuMStqNFFKbsMLjw4ihTiMnR7it/view).
|
|
@ -1,7 +0,0 @@
|
|||||||
FROM python:latest
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY app.py /app/
|
|
||||||
|
|
||||||
CMD ["python", "app.py"]
|
|
@ -1,30 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
# Вариант 2 - Формирует файл /var/result/data.txt из первых строк всех файлов каталога /var/data.
|
|
||||||
def solve(dir_files, dir_result, filename_result):
|
|
||||||
# Получаем список файлов в директории
|
|
||||||
filenames = os.listdir(dir_files)
|
|
||||||
result = ''
|
|
||||||
# Проходим через каждый файл
|
|
||||||
for filename in filenames:
|
|
||||||
filepath = os.path.join(dir_files, filename)
|
|
||||||
file = open(filepath, "r")
|
|
||||||
# Читаем первую строку, добавляем к результату
|
|
||||||
result += f"{file.readline()}"
|
|
||||||
file.close()
|
|
||||||
|
|
||||||
# Если директории для сохранения результата нет - создаём
|
|
||||||
if not os.path.exists(dir_result):
|
|
||||||
os.makedirs(dir_result)
|
|
||||||
# Если директория с результатом не пустая - завершаем работу
|
|
||||||
if os.listdir(dir_result):
|
|
||||||
return
|
|
||||||
# Пишем результат в файл
|
|
||||||
filepath_result = os.path.join(dir_result, filename_result)
|
|
||||||
result_file = open(filepath_result, "w")
|
|
||||||
result_file.write(result)
|
|
||||||
print(f"Результат записан в файл {filepath_result}")
|
|
||||||
result_file.close()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
solve('/var/data', '/var/result', 'data.txt')
|
|
@ -1,7 +0,0 @@
|
|||||||
FROM python:latest
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY app.py /app/
|
|
||||||
|
|
||||||
CMD ["python", "app.py"]
|
|
@ -1,21 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
# Вариант 1 - Ищет набольшее число из файла /var/data/data.txt и сохраняет его вторую степень в /var/result/result.txt.
|
|
||||||
def solve(dir_input, dir_result, filename_result):
|
|
||||||
file_input = open(os.path.join(dir_input, 'data.txt'))
|
|
||||||
# Считываем все числа из файла
|
|
||||||
inputs = [int(line) for line in file_input.readlines()]
|
|
||||||
if inputs:
|
|
||||||
# Максимальное число
|
|
||||||
max_num = max(inputs)
|
|
||||||
print(f"Наибольшее число: {max_num}")
|
|
||||||
# Возводим во 2 степень
|
|
||||||
result = max(inputs) ** 2
|
|
||||||
file_result = open(os.path.join(dir_result, filename_result), "w")
|
|
||||||
# Пишем результат в файл
|
|
||||||
file_result.write(str(result))
|
|
||||||
print(f"Получен результат {result}")
|
|
||||||
file_result.close()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
solve("/var/result", '/var/result', 'result.txt')
|
|
@ -1,7 +0,0 @@
|
|||||||
FROM python:latest
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY generator.py /app/
|
|
||||||
|
|
||||||
CMD ["python", "generate_files.py"]
|
|
@ -1,30 +0,0 @@
|
|||||||
import os
|
|
||||||
import random as rnd
|
|
||||||
import string
|
|
||||||
|
|
||||||
# Генератор названий файлов
|
|
||||||
def generate_filename(l):
|
|
||||||
return ''.join(rnd.choices(string.ascii_lowercase + string.digits, k=l)) + '.txt'
|
|
||||||
|
|
||||||
def generate_files(dir, num_files, num_lines):
|
|
||||||
# Если директории для сохранения файлов нет - создаём
|
|
||||||
if not os.path.exists(dir):
|
|
||||||
os.makedirs(dir)
|
|
||||||
|
|
||||||
# Если директория для сохранения файлов не пустая - завершаем работу
|
|
||||||
if os.listdir(dir):
|
|
||||||
return
|
|
||||||
|
|
||||||
# Создание файлов
|
|
||||||
for i in range(num_files):
|
|
||||||
filename = generate_filename(20)
|
|
||||||
filepath = os.path.join(dir, filename)
|
|
||||||
|
|
||||||
file = open(filepath, "w")
|
|
||||||
# Запись строк в файл
|
|
||||||
for j in range(num_lines):
|
|
||||||
file.write(f"{rnd.randint(-1000, 1000)}\n")
|
|
||||||
file.close()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
generate_files('/var/data', 50, 50)
|
|
@ -1,27 +0,0 @@
|
|||||||
services:
|
|
||||||
# Генератор файлов
|
|
||||||
generator:
|
|
||||||
build:
|
|
||||||
context: ./app-generator # Путь к контексту (докер файл + скрипт)
|
|
||||||
volumes:
|
|
||||||
- ./data:/var/data # Папка контейнера : папка локальная
|
|
||||||
entrypoint: python generator.py # Точка входа
|
|
||||||
|
|
||||||
# Первое приложение
|
|
||||||
app1:
|
|
||||||
build:
|
|
||||||
context: ./app-1 # Путь к контексту
|
|
||||||
volumes:
|
|
||||||
- ./data:/var/data # Монтирование папок
|
|
||||||
- ./result:/var/result
|
|
||||||
depends_on:
|
|
||||||
- generator # Указываем, что запускается только после успешной работы сервиса generator
|
|
||||||
|
|
||||||
# Второе приложение, настройка аналогична сервисам выше
|
|
||||||
app2:
|
|
||||||
build:
|
|
||||||
context: ./app-2
|
|
||||||
volumes:
|
|
||||||
- ./result:/var/result
|
|
||||||
depends_on:
|
|
||||||
- app1
|
|
2
bogdanov_dmitry_lab_3/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
/.idea
|
|
||||||
/.venv
|
|
@ -1,22 +0,0 @@
|
|||||||
# Лабораторная работа №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).
|
|
@ -1,27 +0,0 @@
|
|||||||
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
|
|
@ -1,11 +0,0 @@
|
|||||||
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"]
|
|
@ -1,138 +0,0 @@
|
|||||||
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)
|
|
@ -1,25 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
Flask==3.0.3
|
|
||||||
requests==2.32.3
|
|
@ -1,11 +0,0 @@
|
|||||||
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"]
|
|
@ -1,115 +0,0 @@
|
|||||||
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)
|
|
@ -1,34 +0,0 @@
|
|||||||
# Богданов Дмитрий ПИбд-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)
|
|
@ -1,27 +0,0 @@
|
|||||||
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()
|
|
@ -1,24 +0,0 @@
|
|||||||
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()
|
|
@ -1,25 +0,0 @@
|
|||||||
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()
|
|
@ -1,25 +0,0 @@
|
|||||||
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)
|
|
@ -1,13 +0,0 @@
|
|||||||
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()
|
|
@ -1,19 +0,0 @@
|
|||||||
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()
|
|
@ -1,22 +0,0 @@
|
|||||||
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()
|
|
@ -1,13 +0,0 @@
|
|||||||
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()
|
|
@ -1,22 +0,0 @@
|
|||||||
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()
|
|
@ -1,12 +0,0 @@
|
|||||||
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"
|
|
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 27 KiB |
@ -1,88 +0,0 @@
|
|||||||
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)
|
|
@ -1,18 +0,0 @@
|
|||||||
# Богданов Дмитрий ПИбд-42
|
|
||||||
# Лабораторная работа №5
|
|
||||||
|
|
||||||
|
|
||||||
## Функционал:
|
|
||||||
- Были созданы методы генерации и отображения матриц заданного размера
|
|
||||||
- Былы созданы методы для параллельного умножения матриц с использованием Pool
|
|
||||||
- Был написан код для бенчмаркинга стандартного и параллельного перемножений
|
|
||||||
|
|
||||||
## Результаты выполнения:
|
|
||||||
|
|
||||||
![изображение 1](./images/Screenshot_1.png)
|
|
||||||
|
|
||||||
### Вывод:
|
|
||||||
Использование нескольких потоков приносит значительный выигрыш только на крупных матрицах, в то время как на матрицах меньшего размера больше времени уходит на менеджмент потоков. Это особенно заметно при сравнении результатов выполнения вычислений на матрице размером 100х100.
|
|
||||||
|
|
||||||
|
|
||||||
## [Видео](https://drive.google.com/file/d/1iPfLjzLiWwmszPH_KJ40vFCX-iWDLm1S/view?usp=sharing)
|
|
Before Width: | Height: | Size: 63 KiB |
@ -1,20 +0,0 @@
|
|||||||
# 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
@ -1,33 +0,0 @@
|
|||||||
# Лабораторная работа 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
|
|
@ -1,46 +0,0 @@
|
|||||||
# Версия файла 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:
|
|
@ -1,50 +0,0 @@
|
|||||||
# Отчет. Лабораторная работа 4
|
|
||||||
|
|
||||||
## Описание
|
|
||||||
В ходе лабораторной работы были изучены главы туториала о работе с RabbitMQ. Результат выполнения заданий каждой главы
|
|
||||||
отражен на скриншотах в папке /images:
|
|
||||||
- Tutorial-Task1.png
|
|
||||||
![Tutorial-Task1](images/Tutorial-Task1.png)
|
|
||||||
- Tutorial-Task2.png
|
|
||||||
![Tutorial-Task2](images/Tutorial-Task2.png)
|
|
||||||
- Tutorial-Task3.png
|
|
||||||
![Tutorial-Task3](images/Tutorial-Task3.png)
|
|
||||||
|
|
||||||
Задание из 3-ей главы туториала было расширено условиями, которые были поставлены в задании к данной лабораторной работе.
|
|
||||||
Для демонстрации работы сервисов посредством ассинхронного общения через брокер сообщений RabbitMQ была выбрана
|
|
||||||
предметная область "Обработка заказов".
|
|
||||||
|
|
||||||
Сервис-издатель "Publisher" публикует в очередь сообщений событие поступления заказа с некоторым номером.
|
|
||||||
|
|
||||||
Сервисы-подписчики обрабатывают сообщения о заказах, при этом подписчики обрабатывают сообщение по-разному. Один вид
|
|
||||||
подписчика обрабатывает с задержкой в несколько секунд, другой - "мгновенно", они получают одни и те жа сообщения,
|
|
||||||
но соединены с разными очередями.
|
|
||||||
В качестве эксперимента изначально были запущены по одному экземпляру каждого вида.
|
|
||||||
На изображении Consumer2.png представлена работа мгновенно обрабатывающего подписчика. Он справляется с нагрузкой,
|
|
||||||
так как размер очереди не растет.
|
|
||||||
![Consumer 2](images/Consumer2.png)
|
|
||||||
На изображении Consumer1.png представлена работа подписчика, обрабатывающего сообщения с задержкой. Как мы видим,
|
|
||||||
в очереди накапливаются сообщения в состоянии 'Ready' - эти сообщения готовы для того, чтобы быть доставленными подписчикам.
|
|
||||||
Сервис не справляется с нагрузкой, так как отправляются сообщения быстрее, чем обрабатываются.
|
|
||||||
![Consumer 1](images/Consumer1.png)
|
|
||||||
Для того, чтобы обеспечить равную скорость отправки и обработки, увеличиваем количество экземпляров-подписчиков данного типа до трех.
|
|
||||||
На изображении видно, что теперь длина очереди не растет и система справляется с поступающими сообщениями. Также скорость "publish" и
|
|
||||||
"consumer ack" стали равны.
|
|
||||||
![Consumer 1](images/Consumer1-scaling.png)
|
|
||||||
## Как запустить
|
|
||||||
Для того, чтобы запустить сервисы, необходимо выполнить следующие действия:
|
|
||||||
1. Установить и запустить Docker Engine или Docker Desktop
|
|
||||||
2. Через консоль перейти в папку, в которой расположен файл docker-compose.yml
|
|
||||||
3. Выполнить команду для запуска брокера сообщений rabbitmq:
|
|
||||||
```
|
|
||||||
docker compose up rabbit -d
|
|
||||||
```
|
|
||||||
4. Выполнить команду для запуска остальных контейнеров:
|
|
||||||
```
|
|
||||||
docker compose up -d
|
|
||||||
```
|
|
||||||
Такой порядок запуска важен для того, чтобы брокер сообщений успел полностью запуститься
|
|
||||||
и произвести действия для того, чтобы быть готовым принимать соединения от сервисов. Потому что указания depends_on не хватает
|
|
||||||
для отслеживания завершения всех необходимых подготовительных процессов брокера.
|
|
||||||
## Видео-отчет
|
|
||||||
Работоспособность лабораторной работы можно оценить в следующем [видео](https://disk.yandex.ru/i/G0vsfp7vwazYHw).
|
|
@ -1,23 +0,0 @@
|
|||||||
# Используем образ Maven для сборки
|
|
||||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
|
||||||
|
|
||||||
# Устанавливаем рабочую директорию
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Копируем остальные исходные файлы
|
|
||||||
COPY pom.xml .
|
|
||||||
COPY src src
|
|
||||||
|
|
||||||
# Собираем весь проект
|
|
||||||
RUN mvn clean package -DskipTests
|
|
||||||
RUN mvn dependency:copy-dependencies
|
|
||||||
|
|
||||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
|
||||||
FROM eclipse-temurin:21-jdk-alpine
|
|
||||||
|
|
||||||
# Копируем jar-файл из предыдущего этапа
|
|
||||||
COPY --from=build /app/target/*.jar /app.jar
|
|
||||||
COPY --from=build /app/target/dependency /
|
|
||||||
|
|
||||||
# Указываем команду для запуска приложения
|
|
||||||
CMD ["java", "-jar", "app.jar"]
|
|
@ -1,50 +0,0 @@
|
|||||||
<?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.somecompany</groupId>
|
|
||||||
<artifactId>consumer-app</artifactId>
|
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
|
||||||
<version>3.2.3</version>
|
|
||||||
<relativePath/>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.rabbitmq</groupId>
|
|
||||||
<artifactId>amqp-client</artifactId>
|
|
||||||
<version>5.22.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.30</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
@ -1,15 +0,0 @@
|
|||||||
package ru.somecompany;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
|
||||||
import ru.somecompany.config.property.RabbitProperties;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
@ConfigurationPropertiesScan(basePackageClasses = RabbitProperties.class)
|
|
||||||
public class Main {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(Main.class, args);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
package ru.somecompany.config;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.BuiltinExchangeType;
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
import com.rabbitmq.client.Connection;
|
|
||||||
import com.rabbitmq.client.ConnectionFactory;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import ru.somecompany.config.property.RabbitProperties;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ConnectionFactoryConfig {
|
|
||||||
|
|
||||||
private final RabbitProperties rabbitProperties;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ConnectionFactory connectionFactory() {
|
|
||||||
ConnectionFactory factory = new ConnectionFactory();
|
|
||||||
factory.setHost(rabbitProperties.getHost());
|
|
||||||
factory.setPort(rabbitProperties.getPort());
|
|
||||||
return factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Connection connection(ConnectionFactory connectionFactory) throws IOException, TimeoutException {
|
|
||||||
return connectionFactory.newConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Channel channel(Connection connection) throws IOException {
|
|
||||||
var exchange = rabbitProperties.getExchange();
|
|
||||||
var queue = rabbitProperties.getQueue();
|
|
||||||
var channel = connection.createChannel();
|
|
||||||
|
|
||||||
channel.exchangeDeclare(exchange, BuiltinExchangeType.FANOUT);
|
|
||||||
channel.queueDeclare(queue, true, false, true, null);
|
|
||||||
channel.queueBind(queue, exchange, "");
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package ru.somecompany.config.property;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@ConfigurationProperties(prefix = "app.rabbit-properties")
|
|
||||||
public class RabbitProperties {
|
|
||||||
|
|
||||||
private String host;
|
|
||||||
|
|
||||||
private Integer port;
|
|
||||||
|
|
||||||
private Integer delay;
|
|
||||||
|
|
||||||
private String queue;
|
|
||||||
|
|
||||||
private String exchange;
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
package ru.somecompany.consumer;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.AMQP;
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
import com.rabbitmq.client.Connection;
|
|
||||||
import com.rabbitmq.client.DefaultConsumer;
|
|
||||||
import com.rabbitmq.client.Envelope;
|
|
||||||
import jakarta.annotation.PostConstruct;
|
|
||||||
import jakarta.annotation.PreDestroy;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import ru.somecompany.config.property.RabbitProperties;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class Consumer {
|
|
||||||
|
|
||||||
private final RabbitProperties rabbitProperties;
|
|
||||||
|
|
||||||
private final Connection connection;
|
|
||||||
private final Channel channel;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void consume() {
|
|
||||||
try {
|
|
||||||
channel.basicQos(1);
|
|
||||||
channel.basicConsume(rabbitProperties.getQueue(), false, new DefaultConsumer(channel) {
|
|
||||||
@Override
|
|
||||||
public void handleDelivery(String consumerTag,
|
|
||||||
Envelope envelope,
|
|
||||||
AMQP.BasicProperties properties,
|
|
||||||
byte[] body) throws IOException {
|
|
||||||
long deliveryTag = envelope.getDeliveryTag();
|
|
||||||
|
|
||||||
String message = new String(body, StandardCharsets.UTF_8);
|
|
||||||
System.out.println(" [x] Received '" + message + "'");
|
|
||||||
|
|
||||||
var delay = rabbitProperties.getDelay();
|
|
||||||
try {
|
|
||||||
doWork(delay);
|
|
||||||
} finally {
|
|
||||||
System.out.println(" [x] Processed '" + message + "'");
|
|
||||||
channel.basicAck(deliveryTag, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Exception exception) {
|
|
||||||
log.error("Error while set up connection with rabbit", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doWork(Integer delay) {
|
|
||||||
if (delay > 0) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(delay);
|
|
||||||
} catch (InterruptedException _ignored) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void cleanUp() throws Exception {
|
|
||||||
if (channel != null) {
|
|
||||||
channel.close();
|
|
||||||
}
|
|
||||||
if (connection != null) {
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
server:
|
|
||||||
port: ${SERVER_PORT:8081}
|
|
||||||
|
|
||||||
app:
|
|
||||||
rabbit-properties:
|
|
||||||
host: ${RABBIT_HOST:localhost}
|
|
||||||
port: ${RABBIT_PORT:5672}
|
|
||||||
delay: ${PROCESS_DELAY:0}
|
|
||||||
queue: ${QUEUE_NAME:queue-1}
|
|
||||||
exchange: ${EXCHANGE_NAME:order-events}
|
|
@ -1,79 +0,0 @@
|
|||||||
services:
|
|
||||||
rabbit:
|
|
||||||
container_name: rabbit
|
|
||||||
image: rabbitmq:3-management
|
|
||||||
ports:
|
|
||||||
- "15672:15672"
|
|
||||||
- "5672:5672"
|
|
||||||
- "5671:5671"
|
|
||||||
networks:
|
|
||||||
- local
|
|
||||||
publisher:
|
|
||||||
build: ./publisher-app
|
|
||||||
container_name: publisher
|
|
||||||
depends_on:
|
|
||||||
- rabbit
|
|
||||||
environment:
|
|
||||||
RABBIT_HOST: rabbit
|
|
||||||
RABBIT_PORT: 5672
|
|
||||||
networks:
|
|
||||||
- local
|
|
||||||
consumer-1:
|
|
||||||
build: ./consumer-app
|
|
||||||
container_name: consumer-1
|
|
||||||
depends_on:
|
|
||||||
- rabbit
|
|
||||||
- publisher
|
|
||||||
environment:
|
|
||||||
RABBIT_HOST: rabbit
|
|
||||||
RABBIT_PORT: 5672
|
|
||||||
PROCESS_DELAY: 3000
|
|
||||||
QUEUE_NAME: queue1
|
|
||||||
EXCHANGE_NAME: order-events
|
|
||||||
networks:
|
|
||||||
- local
|
|
||||||
consumer-2:
|
|
||||||
build: ./consumer-app
|
|
||||||
container_name: consumer-2
|
|
||||||
depends_on:
|
|
||||||
- rabbit
|
|
||||||
- publisher
|
|
||||||
environment:
|
|
||||||
RABBIT_HOST: rabbit
|
|
||||||
RABBIT_PORT: 5672
|
|
||||||
PROCESS_DELAY: 0
|
|
||||||
QUEUE_NAME: queue2
|
|
||||||
EXCHANGE_NAME: order-events
|
|
||||||
networks:
|
|
||||||
- local
|
|
||||||
consumer-12:
|
|
||||||
build: ./consumer-app
|
|
||||||
container_name: consumer-12
|
|
||||||
depends_on:
|
|
||||||
- rabbit
|
|
||||||
- publisher
|
|
||||||
environment:
|
|
||||||
RABBIT_HOST: rabbit
|
|
||||||
RABBIT_PORT: 5672
|
|
||||||
PROCESS_DELAY: 3000
|
|
||||||
QUEUE_NAME: queue1
|
|
||||||
EXCHANGE_NAME: order-events
|
|
||||||
networks:
|
|
||||||
- local
|
|
||||||
consumer-13:
|
|
||||||
build: ./consumer-app
|
|
||||||
container_name: consumer-13
|
|
||||||
depends_on:
|
|
||||||
- rabbit
|
|
||||||
- publisher
|
|
||||||
environment:
|
|
||||||
RABBIT_HOST: rabbit
|
|
||||||
RABBIT_PORT: 5672
|
|
||||||
PROCESS_DELAY: 3000
|
|
||||||
QUEUE_NAME: queue1
|
|
||||||
EXCHANGE_NAME: order-events
|
|
||||||
networks:
|
|
||||||
- local
|
|
||||||
|
|
||||||
networks:
|
|
||||||
local:
|
|
@ -1,34 +0,0 @@
|
|||||||
<?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.somecompany</groupId>
|
|
||||||
<artifactId>helloworld-tutorial</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>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.rabbitmq</groupId>
|
|
||||||
<artifactId>amqp-client</artifactId>
|
|
||||||
<version>5.22.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-api</artifactId>
|
|
||||||
<version>1.7.5</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-log4j12</artifactId>
|
|
||||||
<version>1.7.5</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
@ -1,28 +0,0 @@
|
|||||||
package ru.somecompany;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
import com.rabbitmq.client.Connection;
|
|
||||||
import com.rabbitmq.client.ConnectionFactory;
|
|
||||||
import com.rabbitmq.client.DeliverCallback;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
|
|
||||||
private static final String QUEUE_NAME = "hello-world";
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
ConnectionFactory factory = new ConnectionFactory();
|
|
||||||
factory.setHost("localhost");
|
|
||||||
try(Connection connection = factory.newConnection();
|
|
||||||
Channel channel = connection.createChannel();) {
|
|
||||||
var sender = new Sender(channel);
|
|
||||||
var receiver = new Receiver(channel);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println(" [*] Error in Hello-World");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package ru.somecompany;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
import com.rabbitmq.client.DeliverCallback;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class Receiver {
|
|
||||||
|
|
||||||
private static final String QUEUE_NAME = "hello-world";
|
|
||||||
|
|
||||||
public Receiver(Channel channel) throws IOException {
|
|
||||||
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
|
|
||||||
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
|
|
||||||
String message = new String(delivery.getBody(), "UTF-8");
|
|
||||||
System.out.println(" [x] Received '" + message + "'");
|
|
||||||
};
|
|
||||||
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package ru.somecompany;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
public class Sender {
|
|
||||||
|
|
||||||
private static final String QUEUE_NAME = "hello-world";
|
|
||||||
|
|
||||||
public Sender(Channel channel) throws IOException {
|
|
||||||
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
|
|
||||||
String message = "Hello World!";
|
|
||||||
channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
|
|
||||||
System.out.println(" [x] Sent '" + message + "'");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
# Root logger option
|
|
||||||
log4j.rootLogger=INFO, stdout
|
|
||||||
|
|
||||||
# Direct log messages to stdout
|
|
||||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
|
||||||
log4j.appender.stdout.Target=System.out
|
|
||||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
|
||||||
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
|
|
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 848 KiB |
@ -1,38 +0,0 @@
|
|||||||
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
|
|
@ -1,21 +0,0 @@
|
|||||||
# Используем образ Maven для сборки
|
|
||||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
|
||||||
|
|
||||||
# Устанавливаем рабочую директорию
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Копируем остальные исходные файлы
|
|
||||||
COPY pom.xml .
|
|
||||||
COPY src src
|
|
||||||
|
|
||||||
# Собираем весь проект
|
|
||||||
RUN mvn clean package -DskipTests
|
|
||||||
|
|
||||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
|
||||||
FROM eclipse-temurin:21-jdk-alpine
|
|
||||||
|
|
||||||
# Копируем jar-файл из предыдущего этапа
|
|
||||||
COPY --from=build /app/target/*.jar /app.jar
|
|
||||||
|
|
||||||
# Указываем команду для запуска приложения
|
|
||||||
CMD ["java", "-jar", "app.jar"]
|
|
@ -1,49 +0,0 @@
|
|||||||
<?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.somecompany</groupId>
|
|
||||||
<artifactId>publisher-app</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
|
||||||
<version>3.2.3</version>
|
|
||||||
<relativePath/>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.30</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.rabbitmq</groupId>
|
|
||||||
<artifactId>amqp-client</artifactId>
|
|
||||||
<version>5.22.0</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
@ -1,17 +0,0 @@
|
|||||||
package ru.somecompany;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
|
||||||
import ru.somecompany.config.property.RabbitProperties;
|
|
||||||
|
|
||||||
@EnableScheduling
|
|
||||||
@SpringBootApplication
|
|
||||||
@ConfigurationPropertiesScan(basePackageClasses = RabbitProperties.class)
|
|
||||||
public class Main {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(Main.class, args);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package ru.somecompany.config;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.BuiltinExchangeType;
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
import com.rabbitmq.client.Connection;
|
|
||||||
import com.rabbitmq.client.ConnectionFactory;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import ru.somecompany.config.property.RabbitProperties;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ConnectionFactoryConfig {
|
|
||||||
|
|
||||||
private final RabbitProperties rabbitProperties;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ConnectionFactory connectionFactory() {
|
|
||||||
ConnectionFactory factory = new ConnectionFactory();
|
|
||||||
factory.setHost(rabbitProperties.getHost());
|
|
||||||
factory.setPort(rabbitProperties.getPort());
|
|
||||||
return factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Connection connection(ConnectionFactory connectionFactory) throws IOException, TimeoutException {
|
|
||||||
return connectionFactory.newConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Channel channel(Connection connection) throws IOException {
|
|
||||||
var channel = connection.createChannel();
|
|
||||||
channel.exchangeDeclare(rabbitProperties.getExchange(), BuiltinExchangeType.FANOUT);
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package ru.somecompany.config.property;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@ConfigurationProperties(prefix = "app.rabbit-properties")
|
|
||||||
public class RabbitProperties {
|
|
||||||
|
|
||||||
private String host;
|
|
||||||
|
|
||||||
private Integer port;
|
|
||||||
|
|
||||||
private String exchange;
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package ru.somecompany.scheduler;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.BuiltinExchangeType;
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
import com.rabbitmq.client.Connection;
|
|
||||||
import com.rabbitmq.client.ConnectionFactory;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class SenderScheduler {
|
|
||||||
|
|
||||||
private static final String EXCHANGE_NAME = "order-events";
|
|
||||||
private static final String MESSAGE = "Поступил заказ №%d";
|
|
||||||
private Integer index = 0;
|
|
||||||
|
|
||||||
private final ConnectionFactory connectionFactory;
|
|
||||||
private final Connection connection;
|
|
||||||
private final Channel channel;
|
|
||||||
|
|
||||||
@Scheduled(cron = "*/1 * * * * *")
|
|
||||||
public void sendMessage() {
|
|
||||||
try {
|
|
||||||
var message = String.format(MESSAGE, index);
|
|
||||||
|
|
||||||
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes(StandardCharsets.UTF_8));
|
|
||||||
index++;
|
|
||||||
System.out.println(" [x] Sent '" + message + "'");
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.out.println(" [x] Error while send message");
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
server:
|
|
||||||
port: ${SERVER_PORT:8080}
|
|
||||||
|
|
||||||
app:
|
|
||||||
rabbit-properties:
|
|
||||||
host: ${RABBIT_HOST:localhost}
|
|
||||||
port: ${RABBIT_PORT:5672}
|
|
||||||
exchange: ${EXCHANGE_NAME:order-events}
|
|
@ -1,30 +0,0 @@
|
|||||||
<?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.somecompany</groupId>
|
|
||||||
<artifactId>workqueue-tutorial</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>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.rabbitmq</groupId>
|
|
||||||
<artifactId>amqp-client</artifactId>
|
|
||||||
<version>5.22.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-log4j12</artifactId>
|
|
||||||
<version>1.7.5</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
@ -1,23 +0,0 @@
|
|||||||
package ru.somecompany;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
import com.rabbitmq.client.Connection;
|
|
||||||
import com.rabbitmq.client.ConnectionFactory;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
|
|
||||||
public static final String QUEUE_NAME = "task_queue";
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
ConnectionFactory factory = new ConnectionFactory();
|
|
||||||
factory.setHost("localhost");
|
|
||||||
try(Connection connection = factory.newConnection();
|
|
||||||
Channel channel = connection.createChannel();) {
|
|
||||||
var sender = new Sender(channel);
|
|
||||||
sender.send("Work Queue message");
|
|
||||||
var receiver = new Receiver(channel);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println(" [*] Error in Work-Queue: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package ru.somecompany;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
import com.rabbitmq.client.DeliverCallback;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
import static ru.somecompany.Main.QUEUE_NAME;
|
|
||||||
|
|
||||||
public class Receiver {
|
|
||||||
|
|
||||||
public Receiver(Channel channel) throws IOException {
|
|
||||||
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
|
|
||||||
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
|
|
||||||
|
|
||||||
channel.basicQos(1);
|
|
||||||
|
|
||||||
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
|
|
||||||
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
System.out.println(" [x] Received '" + message + "'");
|
|
||||||
try {
|
|
||||||
doWork(message);
|
|
||||||
} finally {
|
|
||||||
System.out.println(" [x] Done");
|
|
||||||
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> { });
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doWork(String task) {
|
|
||||||
for (char ch : task.toCharArray()) {
|
|
||||||
if (ch == '.') {
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException _ignored) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package ru.somecompany;
|
|
||||||
|
|
||||||
import com.rabbitmq.client.Channel;
|
|
||||||
import com.rabbitmq.client.MessageProperties;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
import static ru.somecompany.Main.QUEUE_NAME;
|
|
||||||
|
|
||||||
public class Sender {
|
|
||||||
|
|
||||||
private Channel channel;
|
|
||||||
|
|
||||||
public Sender(Channel channel) throws IOException {
|
|
||||||
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
|
|
||||||
this.channel = channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send(String msg) throws IOException {
|
|
||||||
String message = String.join(" ", msg);
|
|
||||||
|
|
||||||
channel.basicPublish("", QUEUE_NAME,
|
|
||||||
MessageProperties.PERSISTENT_TEXT_PLAIN,
|
|
||||||
message.getBytes("UTF-8"));
|
|
||||||
System.out.println(" [x] Sent '" + message + "'");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
# Root logger option
|
|
||||||
log4j.rootLogger=INFO, stdout
|
|
||||||
|
|
||||||
# Direct log messages to stdout
|
|
||||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
|
||||||
log4j.appender.stdout.Target=System.out
|
|
||||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
|
||||||
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
|
|
@ -1,30 +0,0 @@
|
|||||||
# Отчет. Лабораторная работа 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 с различными конфигурациями запуска (отличался размер умножаемых матриц и параметр отладки),
|
|
||||||
чтобы увидеть результаты выполнения на матрицах всех размеров, необходимых по условию задачи.
|
|
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 29 KiB |