Compare commits

..

1 Commits

Author SHA1 Message Date
ff99375ec7 Готово 2024-11-15 11:50:03 +04:00
2107 changed files with 131 additions and 312866 deletions

View File

@ -7,6 +7,3 @@
/dozorova_alena_lab_2/ConsoleApp2/.vs
/dozorova_alena_lab_2/ConsoleApp2/bin
/dozorova_alena_lab_2/ConsoleApp2/obj
/dozorova_alena_lab_6/ConsoleApp1/.vs
/dozorova_alena_lab_6/ConsoleApp1/bin
/dozorova_alena_lab_6/ConsoleApp1/obj

View File

@ -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 @@
/.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,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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 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,17 +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.uni.rvip</groupId>
<artifactId>matrix-mul</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 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,17 +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.uni.rvip</groupId>
<artifactId>matrix-det</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

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

View File

@ -1,37 +0,0 @@
# Отчет. Лабораторная работа 7
## Балансировка нагрузки в распределённых системах при помощи открытых технологий на примерах
### Какие алгоритмы и методы используются для балансировки нагрузки?
В распределенных системах балансировка нагрузки может осуществляться на разных уровнях:
- балансировка нагрузки на сетевом уровне
- балансировка нагрузки на транспортном уровне
- балансировка нагрузки на прикладном уровне
На сетевом уровне балансировка может реализовываться с помощью таких алгоритмов, как Round Robin, Weighted Round Robin или Least Connections.
Алгоритм Round Robin основывается на принципе выделения одному доменному имени несколько IP, которые выбираются
при поступлении запроса по очереди.
Weighted Round Robin - усовершенствованный алгоритм Round Robin, который подразумевает указание весов каждому серверу
в зависимости от доступных мощностей.
Алгоритм Least Connections вносит в предыдущий алгоритм еще одно условие выбора сервера - количество активных подключений к нему,
избегая перегруженности имеющихся узлов.
### Какие открытые технологии существуют для балансировки нагрузки?
Среди самых популярных открытых технологий можно выделить Nginx и Kubernetes. Nginx позволяет управлять нагрузкой на
компоненты системы с помощью различных алгоритмов и перенаправляет запросы от клиента к сервисам, выстпая в качестве реверс-прокси.
Kubernetes представляет собой платформу для управления контейнерами, которая включает встроенные механизмы для балансировки
нагрузки между подами.
### Как осуществляется балансировка нагрузки на базах данных?
Можно уменьшить нагрузку на базу данных путем использования механизма кэширования на уровне приложения
или с помощью нереляционных БД (Redis и др.). Или с помощью шардирования данных, размещенных в базе.
Для балансировки нагрузки на базах данных на прикладном уровне, например, существует утилита pgpool —
прокси между клиентом и сервером СУБД PostgreSQL, с помощью которого задаются правила перенаправления
запросов к БД в зависимости от их содержания (чтение/запись).
### Реверс-прокси как один из элементов балансировки нагрузки.
Реверс-прокси перенаправляет входящие запросы на соотвествующие сервисы, может выступать при этом и в качестве балансировщика нагрузки, реализуя один из ранее озвученных алгоритмов.
Как уже было сказано, примером такого компонента РС может выступать Nginx, в котором с помощью настройки конфигурационного файла можно указать как настройки проксирования,
так и добавить логику балансировки.

View File

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

View File

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

View File

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

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35004.147
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsumerDelay", "ConsumerDelay.csproj", "{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DD86D5F-D90D-4BBB-AAA4-F16DA855B51E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E7AED20-0868-42FE-9C39-581BC9D2BB22}
EndGlobalSection
EndGlobal

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35004.147
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsumerSimple", "ConsumerSimple.csproj", "{ACA8DE52-E29E-41BA-B3DA-213AF316685E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ACA8DE52-E29E-41BA-B3DA-213AF316685E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACA8DE52-E29E-41BA-B3DA-213AF316685E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACA8DE52-E29E-41BA-B3DA-213AF316685E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACA8DE52-E29E-41BA-B3DA-213AF316685E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {73265D6C-436C-470E-AE8A-17047E6C2ECC}
EndGlobalSection
EndGlobal

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35004.147
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Publisher", "Publisher.csproj", "{C23890FA-A4DD-4E5B-897F-37210C2F60CE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C23890FA-A4DD-4E5B-897F-37210C2F60CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C23890FA-A4DD-4E5B-897F-37210C2F60CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C23890FA-A4DD-4E5B-897F-37210C2F60CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C23890FA-A4DD-4E5B-897F-37210C2F60CE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3BB20EB3-DE49-46CE-8C7A-D956E3DE90BC}
EndGlobalSection
EndGlobal

View File

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

View File

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

View File

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

View File

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

View File

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

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