Compare commits

..

No commits in common. "main" and "tukaeva_alfiya_lab_3" have entirely different histories.

2174 changed files with 0 additions and 316385 deletions

View File

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

View File

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

View File

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

View File

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

View File

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

View File

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

View File

@ -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 для базы данных

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
2

View File

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

View File

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

View File

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

View File

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

View File

@ -1,2 +0,0 @@
data/
result/

View File

@ -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).

View File

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

View File

@ -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')

View File

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

View File

@ -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')

View File

@ -1,7 +0,0 @@
FROM python:latest
WORKDIR /app
COPY generator.py /app/
CMD ["python", "generate_files.py"]

View File

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

View File

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

View File

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

View File

@ -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).

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

View File

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

View File

View File

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

View File

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

View File

@ -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).

View File

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

View File

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

View File

@ -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);
}
}

View File

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

View File

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

View File

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

View File

@ -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}

View File

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

View File

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

View File

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

View File

@ -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 -> { });
}
}

View File

@ -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 + "'");
}
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 848 KiB

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

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

View File

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

View File

@ -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);
}
}
}

View File

@ -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}

View File

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

View File

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

View File

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

View File

@ -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 + "'");
}
}

View File

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

View File

@ -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 с различными конфигурациями запуска (отличался размер умножаемых матриц и параметр отладки),
чтобы увидеть результаты выполнения на матрицах всех размеров, необходимых по условию задачи.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

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