This commit is contained in:
S3 2024-12-08 17:47:01 +03:00
parent bc087de470
commit 9ddb3d475d
37 changed files with 1225 additions and 0 deletions

BIN
davydov_yuriy_lab_1/1.mp4 Normal file

Binary file not shown.

View File

@ -0,0 +1,45 @@
services:
mediawiki:
image: mediawiki
container_name: mediawiki
ports:
- "8080:80"
volumes:
- mediawiki_data:/var/www/html/images
environment:
- MEDIAWIKI_DB_HOST=database
- MEDIAWIKI_DB_NAME=mediawiki
- MEDIAWIKI_DB_USER=root
- MEDIAWIKI_DB_PASSWORD=example
depends_on:
- database
redmine:
image: redmine
container_name: redmine
ports:
- "8081:3000"
volumes:
- redmine_data:/usr/src/redmine/files
environment:
- REDMINE_DB_POSTGRESQL=database
- REDMINE_DB_DATABASE=redmine
- REDMINE_DB_USERNAME=root
- REDMINE_DB_PASSWORD=example
depends_on:
- database
database:
image: postgres:latest
container_name: database
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: example
POSTGRES_DB: postgres
volumes:
- database_data:/var/lib/postgresql
volumes:
mediawiki_data:
redmine_data:
database_data:

View File

@ -0,0 +1,38 @@
# Лабораторная работа №1 - Знакомство с docker и docker-compose
## ПИбд-42 || Давыдов Юрий
### Описание
В процессе выполнения лабораторной работы было развернуты сервисы с применением docker-compose. MediaWiki (движок вики) и Readmine (баг-трекер). Каждый сервис функционирует в своем контейнере, используя общую базу данных для хранения данных на объектно-реляционной СУБД PostgreSQL. Был настроен проброс портов для успешного доступа к веб-интерфейсам указанных сервисов. Docker volumes применяется для того, чтобы сохранять данных вне контейнеров.
### Цель лабораторной работы
изучение современных технологий контейнеризации
### Инструкция для работы:
1. Клонирование репозитория:
```
git clone <ссылка-на-репозиторий>
cd <папка репозитория>
cd <папка лабораторной работы>
```
2. Запуск контейнеров:
```
docker-compose up -d
```
3. При успешном запуске станут доступны такие контейнеры:
*MediaWiki: http://localhost:8080
*Redmine: http://localhost:8081
4. Команда для остановки контейнеров:
```
docker-compose down
```

BIN
davydov_yuriy_lab_2/2.mp4 Normal file

Binary file not shown.

View File

@ -0,0 +1,91 @@
# Лабораторная работа №2 - Разработка простейшего распределенного приложения
## ПИбд-42 || Давыдов Юрий
## Описание
В рамках данной лабораторной работы было создано два контейнера спростыми программами на python, соблюдающими условие: результат первой программы - это исходные данные второй программы.
### Цель лабораторной работы
изучение техники создания простого распределённого приложения
### 1. Варианты
Для обеих программ был выбран вариант 1.
Вариант первой программы:
**1**. Ищет в каталоге /var/data файл с наибольшим количеством строк и перекладывает его в /var/result/data.txt.
Вариант второй программы:
**1**. Ищет набольшее число из файла /var/data/data.txt и сохраняет его вторую степень в /var/result/result.txt.
### 2. Dockerfile
Два Dockerfile имеют сходную структуру:
```Dockerfile
#Берем базовый образ python
FROM python:3.12
#Устанавливаем рабочую директорию
WORKDIR /app
#Копирум код в рабочую директорию
COPY main.py .
#Задаем /var/data как монтируемый
VOLUME ["/var/data"]
#Задаем /var/result как монтируемый
VOLUME ["/var/result"]
#Задаем команду для выполнения программы
CMD ["python", "main.py"]
```
### 3. Docker-compose
`docker-compose.yml`:
```yaml
services:
#Первая программа
first:
#Директория для сборки первой программы
build: ./WorkFirst/
#Монтирование 2 каталогов из хост системы
volumes:
- ./data:/var/data
- ./result_first:/var/result
#Вторая программа
second:
#Директория для сборки второй программы
build: ./WorkSecond/
#Задание очередности запуска через depends_on
depends_on:
- first
#Монтирование 2 каталогов из хост системы
volumes:
- ./result_first:/var/data
- ./result_second:/var/result
```
### 4. Инструкция для работы
1. Клонирование репозитория:
```
git clone <ссылка-на-репозиторий>
cd <папка репозитория>
cd <папка лабораторной работы>
```
2. Запуск контейнеров:
```
docker compose up --build
```
3. Результаты:
Итог работы первой программы будет в папке `result_first`, а второй - в `result_second`.

View File

@ -0,0 +1,16 @@
#Берем базовый образ python
FROM python:3.12
#Устанавливаем рабочую директорию
WORKDIR /app
#Копирум код в рабочую директорию
COPY main.py .
#Задаем /var/data как монтируемый
VOLUME ["/var/data"]
#Задаем /var/result как монтируемый
VOLUME ["/var/result"]
#Задаем команду для выполнения программы
CMD ["python", "main.py"]

View File

@ -0,0 +1,81 @@
import os
import random
# Путь к каталогу для поиска файла с наибольшим кол-вом строк
CATALOG_PATH = "/var/data"
# Путь до файла с результатом
RESULT_FILE = "/var/result/data.txt"
def find_file_with_most_lines(directory):
"""Ищет файл с наибольшим количеством строк в заданном каталоге и его подкаталогах."""
file_with_most_lines = None
max_lines = 0
for root, _, files in os.walk(directory):
for file in files:
filepath = os.path.join(root, file)
try:
with open(filepath, 'r', encoding='utf-8') as f:
line_count = sum(1 for _ in f)
if line_count > max_lines:
max_lines = line_count
file_with_most_lines = (filepath, line_count)
except (OSError, UnicodeDecodeError) as e:
print(f"Ошибка при обработке файла '{filepath}': {e}")
return file_with_most_lines
def copy_file(first, second):
"""Копирует содержимое файла first в файл second."""
try:
with open(second, "wb") as f_second, open(first, "rb") as f_first:
while chunk := f_first.read(4096):
f_second.write(chunk)
print(f"Файл '{first}' успешно скопирован в '{second}'.")
except Exception as e:
print(f"Ошибка при копировании файла '{first}': {e}")
def main():
file_with_most_lines = find_file_with_most_lines(CATALOG_PATH)
if file_with_most_lines:
first_path, _ = file_with_most_lines
second_path = RESULT_FILE
copy_file(first_path, second_path)
else:
print("Не найдены файлы")
def generate_random_numbers(filename, count):
"""Функция генерирует случайные числа и записывает их в файл"""
with open(filename, "w") as f:
for _ in range(count):
num = random.randint(0, 1000)
f.write(str(num) + "\n")
print(f"Случайные числа успешно записаны в '{filename}'.")
if __name__ == "__main__":
generate_random_numbers("/var/data/data1.txt", 25)
generate_random_numbers("/var/data/data2.txt", 30)
generate_random_numbers("/var/data/data3.txt", 27)
generate_random_numbers("/var/data/data4.txt", 12)
generate_random_numbers("/var/data/data5.txt", 19)
print("Генерация файлов завершена")
main()

View File

@ -0,0 +1,16 @@
#Берем базовый образ python
FROM python:3.12
#Устанавливаем рабочую директорию
WORKDIR /app
#Копирум код в рабочую директорию
COPY main.py .
#Задаем /var/data как монтируемый
VOLUME ["/var/data"]
#Задаем /var/result как монтируемый
VOLUME ["/var/result"]
#Задаем команду для выполнения программы
CMD ["python", "main.py"]

View File

@ -0,0 +1,60 @@
# Путь к файлу для чтения
DATA_FILE = "/var/data/data.txt"
# Путь к файлу для записи
RESULT_FILE = "/var/result/result.txt"
def find_biggest_number(data_file):
"""Поиск наибольшего числа в файле"""
try:
with open(data_file, "r") as f:
numbers = [int(line.strip()) for line in f.readlines()]
biggest_num = max(numbers)
return biggest_num
except ValueError as e:
print(f"Ошибка преобразования строки: {e}")
except Exception as e:
print(f"Ошибка чтения файла '{data_file}': {e}")
return None
def square_number(num):
"""Возвращает квадрат числа"""
return num**2
def write_result(result_file, result):
"""Фиксирует результат в файл."""
try:
with open(result_file, "w") as f:
f.write(str(result))
print(f"Результат '{result}' успешно передан в '{result_file}'.")
except Exception as e:
print(f"Ошибка фиксации результата в файл '{result_file}': {e}")
def main():
biggest_num = find_biggest_number(DATA_FILE)
if biggest_num is not None:
result = square_number(biggest_num)
write_result(RESULT_FILE, result)
print(result)
else:
print("Нет чисел в файле.")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,21 @@
services:
#Первая программа
first:
#Директория для сборки первой программы
build: ./WorkFirst/
#Монтирование 2 каталогов из хост системы
volumes:
- ./data:/var/data
- ./result_first:/var/result
#Вторая программа
second:
#Директория для сборки второй программы
build: ./WorkSecond/
#Задание очередности запуска через depends_on
depends_on:
- first
#Монтирование 2 каталогов из хост системы
volumes:
- ./result_first:/var/data
- ./result_second:/var/result

BIN
davydov_yuriy_lab_3/3.mp4 Normal file

Binary file not shown.

View File

@ -0,0 +1,39 @@
# Лабораторная работа №3 - REST API, Gateway и синхронный обмен между микросервисами
## ПИбд-42 || Давыдов Юрий
### Описание:
В рамках данной лабораторной работы были реализованы две сущности со связью "один-ко-многим", также были разработаны все CRUD-операции: получение списка записей, получение одной записи, создание, редактирование и удаление записи.
У сущности факультета также реализована возможность получения записи со списком всх специальностей. В этом случае сервис взаимодействует с другим сервисом. Еще реализована проверка наличия факультета по id.
У сущности специальности также реализованы возможности: получение списка записей с информацией о факультете, получение списка записей по id факультета и получение одной записи с информацией о факультете.
### Цель лабораторной работы
изучение шаблона проектирования gateway, построения синхронного обмена между микросервисами и архитектурного стиля RESTful API
### Выбранные сущности:
- Факультет. Поля: Uuid, Name и Description
- Специальность. Поля: Uuid, Name, CountPlaces и Faculty_Id
### Инструкция для работы
1. Клонирование репозитория:
```
git clone <ссылка-на-репозиторий>
cd <папка репозитория>
cd <папка лабораторной работы>
```
2. Запуск контейнеров:
```
docker compose up --build
```
3. Результаты:
В результате можно будет применят CRUD операции к сущностям через http запросы. Для демонстрации был выбран знакомый инструмент Postman.

View File

@ -0,0 +1,27 @@
services:
faculty_service:
container_name: faculty_service
build:
context: .
dockerfile: ./faculty_service/Dockerfile
expose:
- 10001
speciality_service:
container_name: speciality_service
build:
context: .
dockerfile: ./speciality_service/Dockerfile
expose:
- 10002
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- faculty_service
- speciality_service

View File

@ -0,0 +1,17 @@
#Берем базовый образ python
FROM python:3.12
#Устанавливаем рабочую директорию
WORKDIR /app
#Копируем requirements.txt в контейнер
COPY requirements.txt .
#Подтягиваем зависимости
RUN pip install --no-cache-dir -r requirements.txt
#Копируем файлы программы в контейнер
COPY faculty_service/faculty_service.py .
#Задаем команду для выполнения программы
CMD ["python", "faculty_service.py"]

View File

@ -0,0 +1,129 @@
from flask import Flask, jsonify, request
from uuid import uuid4
import uuid
import requests
class Faculty:
def __init__(self, name, description, uuid_: uuid):
if uuid_ is None:
self.uuid_: uuid = uuid4()
else:
self.uuid_: uuid = uuid.UUID(uuid_)
self.name: str = name
self.description: str = description
def to_dict(self):
return {
"uuid": self.uuid_,
"name": self.name,
"description": self.description
}
def to_dict_with_specialities(self, specialities: list):
return {
"uuid": self.uuid_,
"name": self.name,
"description": self.description,
"specialities": specialities
}
app = Flask(__name__)
faculties: list[Faculty] = [
Faculty(name='ФИСТ', description='Факультет информационных систем и технологий', uuid_='3c87a9a1-1a3e-402d-8a53-6bbba478e644'),
Faculty(name='ГФ', description='Гуманитарный факультет', uuid_='a9e36307-02dc-4ee0-a771-e6d39cae9a75'),
Faculty(name='РТФ', description='Радиотехнический факультет', uuid_='3e7fc35d-f3bb-4657-bdcf-cf19191f3bc0'),
Faculty(name='ИЭФ', description='Инженерно-экономический факультет', uuid_='bb8571c4-f4a9-4d6e-b1f3-6efe4f54166e')
]
specialities_url = 'http://speciality_service:10002/'
def list_jsonify():
return jsonify([faculty.to_dict() for faculty in faculties])
# получение списка всех факультетов
@app.route('/', methods=['GET'])
def get_all():
return list_jsonify(), 200
# получение факультета по id
@app.route('/<uuid:uuid_>', methods=['GET'])
def get_one(uuid_):
for faculty in faculties:
if faculty.uuid_ == uuid_:
return faculty.to_dict(), 200
return 'Факультет с указанным uuid не обнаружен', 404
# получение факультета со списком специальностей
@app.route('/with-specialities/<uuid:uuid_>', methods=['GET'])
def get_one_with_specialities(uuid_):
for faculty in faculties:
if faculty.uuid_ == uuid_:
response = requests.get(specialities_url + f'by-faculty/{uuid_}')
print(response.json())
return faculty.to_dict_with_specialities(response.json()), 200
return 'Факультет с указанным uuid не обнаружен', 404
# проверка наличия факультета по id (для специальности)
@app.route('/check/<uuid:uuid_>', methods=['GET'])
def check_exist(uuid_):
for faculty in faculties:
if faculty.uuid_ == uuid_:
return '', 200
return '', 404
# создание факультета
@app.route('/', methods=['POST'])
def create():
data = request.json
name = data.get('name', None)
description = data.get('description', None)
if name is None or description is None:
return 'Не хватает данных для создания нового факультета', 404
new_faculty = Faculty(name, description, None)
faculties.append(new_faculty)
return get_one(new_faculty.uuid_)
# изменение факультета по id
@app.route('/<uuid:uuid_>', methods=['PUT'])
def update_by_id(uuid_):
data = request.json
new_name = data.get('name', None)
new_description = data.get('description', None)
for faculty in faculties:
if faculty.uuid_ == uuid_:
if new_name is not None:
faculty.name = new_name
if new_description is not None:
faculty.description = new_description
return get_one(faculty.uuid_)
return 'Факультет с указанным uuid не обнаружен', 404
# удаление факультета по id
@app.route('/<uuid:uuid_>', methods=['DELETE'])
def delete(uuid_):
for faculty in faculties:
if faculty.uuid_ == uuid_:
faculties.remove(faculty)
return 'Факультет был удален', 200
return 'Факультет с указанным uuid не обнаружен', 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=10001, debug=True)

View File

@ -0,0 +1,26 @@
events { worker_connections 1024; }
http {
server {
listen 80;
listen [::]:80;
server_name localhost;
location /faculty_service/ {
proxy_pass http://faculty_service:10001/;
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 /speciality_service/ {
proxy_pass http://speciality_service:10002/;
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;
}
}
}

Binary file not shown.

View File

@ -0,0 +1,17 @@
#Берем базовый образ python
FROM python:3.12
#Устанавливаем рабочую директорию
WORKDIR /app
#Копируем requirements.txt в контейнер
COPY requirements.txt .
#Подтягиваем зависимости
RUN pip install --no-cache-dir -r requirements.txt
#Копируем файлы программы в контейнер
COPY speciality_service/speciality_service.py .
#Задаем команду для выполнения программы
CMD ["python", "speciality_service.py"]

View File

@ -0,0 +1,161 @@
from flask import Flask, jsonify, request
import requests
from uuid import uuid4
import uuid
class Speciality:
def __init__(self, name, countPlaces, uuid_: uuid, faculty_id: uuid):
if uuid_ is None:
self.uuid_: uuid = uuid4()
else:
self.uuid_: uuid = uuid.UUID(uuid_)
self.name: str = name
self.countPlaces: int = countPlaces
self.faculty_id: uuid = uuid.UUID(faculty_id)
def to_dict(self):
return {
'name': self.name,
'countPlaces': self.countPlaces,
'faculty_id': self.faculty_id,
'uuid': self.uuid_
}
def to_dict_for_faculties(self):
return {
'name': self.name,
'countPlaces': self.countPlaces,
'uuid': self.uuid_
}
def to_dict_with_info(self, faculty: dict):
return {
'name': self.name,
'countPlaces': self.countPlaces,
'faculty_id': self.faculty_id,
'faculty_info': faculty,
'uuid': self.uuid_
}
app = Flask(__name__)
specialities: list[Speciality] = [
Speciality(name='Программная инженерия', countPlaces=39, uuid_='a6233a8e-7b54-4927-954a-cafbdbf142fe',
faculty_id='3c87a9a1-1a3e-402d-8a53-6bbba478e644'),
Speciality(name='Прикладная информатика', countPlaces=53, uuid_='c1863dee-7b27-4252-9327-9226dd10eebe',
faculty_id='3c87a9a1-1a3e-402d-8a53-6bbba478e644'),
Speciality(name='Лингвистика', countPlaces=23, uuid_='da1f1594-39c7-4407-8c54-883e9a8565ea',
faculty_id='a9e36307-02dc-4ee0-a771-e6d39cae9a75'),
Speciality(name='Радиотехника', countPlaces=46, uuid_='a7156dbf-b102-40bd-9eb9-d53c110cfe0d',
faculty_id='603e552f-2b53-4d60-b13b-700efa4dfbb9'),
Speciality(name='Управление качеством', countPlaces=17, uuid_='1a24d9b7-72d2-4918-8a86-b80608c855f6',
faculty_id='3e7fc35d-f3bb-4657-bdcf-cf19191f3bc0')
]
facuties_url = 'http://faculty_service:10001/'
def list_jsonify():
return jsonify([speciality.to_dict() for speciality in specialities])
# получить список специальностей
@app.route('/', methods=['GET'])
def get_all():
return list_jsonify(), 200
# получить список специальностей с информацией о факультете
@app.route('/full', methods=['GET'])
def get_all_full():
faculties: list[dict] = requests.get(facuties_url).json()
response = []
for speciality in specialities:
for faculty in faculties:
if speciality.faculty_id == uuid.UUID(faculty.get('uuid')):
response.append(speciality.to_dict_with_info(faculty))
return response, 200
# получение списка специальностей по id факультета
@app.route('/by-faculty/<uuid:faculty_uuid>', methods=['GET'])
def get_by_faculty_id(faculty_uuid):
return [speciality.to_dict_for_faculties() for speciality in specialities if speciality.faculty_id == faculty_uuid], 200
# получение специальности по id
@app.route('/<uuid:uuid_>', methods=['GET'])
def get_one(uuid_):
for speciality in specialities:
if speciality.uuid_ == uuid_:
return speciality.to_dict(), 200
return 'Специальность с указанным uuid не обнаружена', 404
# получение специальности по id с информацией о факультете
@app.route('/full/<uuid:uuid_>', methods=['GET'])
def get_one_full(uuid_):
for speciality in specialities:
if speciality.uuid_ == uuid_:
response = requests.get(facuties_url + str(speciality.faculty_id))
return speciality.to_dict_with_info(response.json()), 200
return 'Специальность с указанным uuid не обнаружена', 404
# создание новой специальности
@app.route('/', methods=['POST'])
def create():
data = request.json
name = data.get('name', None)
countPlaces = data.get('countPlaces', None)
faculty_id = data.get('faculty_id', None)
checking = requests.get(facuties_url + f'/check/{faculty_id}')
print(checking)
if checking.status_code == 200:
new_speciality = Speciality(name, countPlaces, None, faculty_id)
specialities.append(new_speciality)
return get_one(new_speciality.uuid_)
if checking.status_code == 404:
return 'Факультета с таким uuid не существует', 404
return 'Неизвестная ошибка', 500
# изменение специальности по id
@app.route('/<uuid:uuid_>', methods=['PUT'])
def update_by_id(uuid_):
data = request.json
new_name = data.get('name', None)
new_countPlaces = data.get('countPlaces', None)
for speciality in specialities:
print(speciality.uuid_)
if speciality.uuid_ == uuid_:
if new_name is not None:
speciality.name = new_name
if new_countPlaces is not None:
speciality.countPlaces = new_countPlaces
return get_one(speciality.uuid_)
return 'Специальность с указанным uuid не обнаружена', 404
# удаление специальности по id
@app.route('/<uuid:uuid_>', methods=['DELETE'])
def delete(uuid_):
for speciality in specialities:
if speciality.uuid_ == uuid_:
specialities.remove(speciality)
return 'Специальность была удалена', 200
return 'Специальность с указанным uuid не обнаружена', 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=10002, debug=True)

View File

@ -0,0 +1,25 @@
import random
import time
import pika
queue_name = 'first_queue'
exchange = 'logs'
def callback(ch, method, properties, body):
print(f" Consumer_1: было получено сообщение {body.decode()}")
time.sleep(random.choice([2, 3]))
print(f" Consumer_1: было обработано сообщение")
ch.basic_ack(delivery_tag=method.delivery_tag)
if __name__ == '__main__':
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
try:
channel.queue_declare(queue=queue_name) #queue
channel.queue_bind(exchange=exchange, queue=queue_name) #binding
channel.basic_consume(queue=queue_name, on_message_callback=callback)
channel.start_consuming()
except KeyboardInterrupt:
connection.close()

View File

@ -0,0 +1,22 @@
import pika
queue_name = 'second_queue'
exchange = 'logs'
def callback(ch, method, properties, body):
print(f" Consumer_2: было получено сообщение {body.decode()}")
print(f" Consumer_2: было обработано сообщение")
ch.basic_ack(delivery_tag=method.delivery_tag)
if __name__ == '__main__':
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
try:
channel.queue_declare(queue=queue_name) #queue
channel.queue_bind(exchange=exchange, queue=queue_name) #binding
channel.basic_consume(queue=queue_name, on_message_callback=callback)
channel.start_consuming()
except KeyboardInterrupt:
connection.close()

View File

@ -0,0 +1,18 @@
import random
import time
import pika
if __name__ == '__main__':
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs', exchange_type='fanout')
try:
while True:
message = random.choice(["Необходимо подать заявление на поступление", "Обновлена информация о факультетах",
"Обновлена информация о специальностях", "Изменение в конкурсных списках", "Начат доприем",
"Приемная комиссия закрывается"])
channel.basic_publish(exchange='logs', routing_key='', body=message)
time.sleep(1)
except KeyboardInterrupt:
connection.close()

View File

@ -0,0 +1,44 @@
# Лабораторная работа №4 - Работа с брокером сообщений
## ПИбд-42 || Давыдов Юрий
### Цель лабораторной работы
Изучение проектирования приложений при помощи брокера сообщений.
### Описание задач 1-2:
В рамках данной лабораторной работы был установлен брокер сообщений RabbitMQ. После этого для обучения были пройдены первые три урока из RabbitMQ Tutorials на ЯП Python. Результаты приложены в виде скриншотов:
- Итоги первого урока:
![изображение 1](./pictures/img_lesson_1.png)
- Итоги второго урока:
![изображение 2](./pictures/img_lesson_2.png)
- Итоги третьего урока:
![изображение 3](./pictures/img_lesson_3.png)
### Предметная область:
В качестве предметной области применяется область из прошлой лабораторной работы - списки абитуриентов для поступления в университет.
### Описание задачи 3:
Далее в рамках лабораторной работы по предметной области были разработаны демонстрационные приложения Publisher, Consumer_1 и Consumer_2. Результаты их работы:
- Данные по очереди first_queue, запущен один экземпляр Consumer_1:
![изображение 1](./pictures/1_Consumer1.png)
- Данные по очереди second_queue, запущен один экземпляр Consumer_2:
![изображение 2](./pictures/Consumer2.png)
- Данные по очереди first_queue, запущено два экземпляра Consumer_1:
![изображение 3](./pictures/2_Consumer1.png)
- Данные по очереди first_queue, запущено три экземпляра Consumer_1:
![изображение 4](./pictures/3_Consumer1.png)
### Выводы:
По представленным данным хорошо видно, что second_queue не заполняется совсем, это связано с тем, что обработка сообщений там моментальная и в очереди им задерживаться не приходится. В свою очередь, Cunsomer_1 тратит на обработку сообщения 2-3 секунды, поэтому очередь first_queue достаточно быстро заполняется, если запущен один экземпляр. Также было обнаружено, что чем больше экземпляров Cunsomer_1 запущено, тем медленнее заполняется очередь. Это потому, что каждый из экземпляров занимается обработкой сообщений из очереди. С увеличением числа экземпляров можно достичь момента, когда очередь перестанет заполняться.

View File

@ -0,0 +1,25 @@
import pika, sys, os
def main():
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello')
def callback(ch, method, properties, body):
print(f" [x] Received {body}")
channel.basic_consume(queue='hello', on_message_callback=callback, auto_ack=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('Interrupted')
try:
sys.exit(0)
except SystemExit:
os._exit(0)

View File

@ -0,0 +1,11 @@
import pika
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello')
channel.basic_publish(exchange='', routing_key='hello', body='Hello World!')
print(" [x] Sent 'Hello World!'")
connection.close()

View File

@ -0,0 +1,19 @@
import pika
import sys
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='task_queue', durable=True)
message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(
exchange='',
routing_key='task_queue',
body=message,
properties=pika.BasicProperties(
delivery_mode=pika.DeliveryMode.Persistent
))
print(f" [x] Sent {message}")
connection.close()

View File

@ -0,0 +1,22 @@
import pika
import time
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='task_queue', durable=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
def callback(ch, method, properties, body):
print(f" [x] Received {body.decode()}")
time.sleep(body.count(b'.'))
print(" [x] Done")
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_qos(prefetch_count=1)
channel.basic_consume(queue='task_queue', on_message_callback=callback)
channel.start_consuming()

View File

@ -0,0 +1,13 @@
import pika
import sys
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs', exchange_type='fanout')
message = ' '.join(sys.argv[1:]) or "info: Hello World!"
channel.basic_publish(exchange='logs', routing_key='', body=message)
print(f" [x] Sent {message}")
connection.close()

View File

@ -0,0 +1,22 @@
import pika
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
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()

BIN
davydov_yuriy_lab_5/5.mp4 Normal file

Binary file not shown.

View File

@ -0,0 +1,11 @@
# Лабораторная работа №5 - Параллельное перемножение матриц
## ПИбд-42 || Давыдов Юрий
### Цель лабораторной работы
Изучение принципов работы праллельных вычислений, когда они оправданы, а когда нет.
### Описание:
Был реализован механизм параллельного перемножения матриц 100x100, 300x300 и 500x500 с возможностью задания потоков, в том числе и 1 (последовательное перемножение). Были сделаны замеры времени для каждого вычисления, проведен анализ и сделаны выводы.

View File

@ -0,0 +1,58 @@
import random
import time
import multiprocessing
import numpy as np
# Генерация матрицы
def generate_matrix(size):
return [[random.randint(0, 10) for _ in range(size)] for _ in range(size)]
# Умножение одной строки
def multiply_row(i, A, B, result):
size = len(A)
for j in range(size):
for k in range(size):
result[i][j] += A[i][k] * B[k][j]
# параллельное умножение матриц с помощью multiprocessing
def parallel_matrix_multiply(A, B, num_processes):
size = len(A)
result = [[0] * size for _ in range(size)]
with multiprocessing.Pool(processes=num_processes) as pool:
pool.starmap(multiply_row, [(i, A, B, result) for i in range(size)])
return result
# Замер времени на умножение
def benchmark(size, num_processes=1):
A = generate_matrix(size)
B = generate_matrix(size)
start_time = time.time()
parallel_matrix_multiply(A, B, num_processes)
par_time = time.time() - start_time
return par_time
def main():
# Размеры матриц
matrix_sizes = [100, 300, 500]
# Количество потоков
num_processes_list = [1, 2, 4, 6, 8]
# Таблица с бенчмарками
print("-*" * 40)
print(f"{'Количество потоков':<20}{'|100x100 (сек.)':<20}{'|300x300 (сек.)':<20}{'|500x500 (сек.)'}")
print("-*" * 40)
for num_processes in num_processes_list:
row = f"{num_processes:<20}"
for size in matrix_sizes:
par_time = benchmark(size, num_processes)
row += f"|{par_time:.4f}".ljust(20)
print(row)
print("-*" * 40)
if __name__ == "__main__":
main()

BIN
davydov_yuriy_lab_6/6.mp4 Normal file

Binary file not shown.

View File

@ -0,0 +1,22 @@
# Лабораторная работа №6 - Параллельный поиск значения детерминанта матрицы
## ПИбд-42 || Давыдов Юрий
### Цель лабораторной работы
Изучение принципов работы праллельных вычислений, когда они оправданы, а когда нет.
### Описание:
Был реализован механизм для параллельного вычисления детерминанта матриц с возможностью задания потоков, в том числе и 1 (обычное вычисление). Был применен на матрицах размером 9x9, 10x10 и 11x11. Были сделаны замеры времени для каждого вычисления, проведен анализ и сделаны выводы.
### Результаты:
![Изображение 1](./results.png)
### Выводы:
При параллельном поиске детерминанта мы нацелены уменьшить временные затраты за счет увеличения числа потоков. Это действительно дает свои плоды, но есть некоторые нюансы.
Из результатов видно, что для вычисления детерминанта матрицы в одном потоке потребовалось 322 секунды, тогда как в 8 потоках время составило 142 секунды, а это значит, что мы произвели вычисления более чем вдвое быстрее.
При этом при применении такого подхода к малым вычислениям, мы наоборот можем просесть по затратам, т. к. для управления многопоточностью тоже требуются ресурсы, поэтому определять целесообразность разбиения задачи на потоки следует исходя из ее объема.
Следующий момент - это определение оптимального числа потоков, не всегда больше = лучше, ведь на менеджмент новых потоков также придется тратить ресурсы.

View File

@ -0,0 +1,84 @@
import random
import time
import multiprocessing
import numpy as np
# Генерация матрицы
def generate_matrix(size):
return [[random.randint(0, 10) for _ in range(size)] for _ in range(size)]
# Вычисление детерминанта матрицы (рекурсивно)
def determinant(matrix):
size = len(matrix)
if size == 2:
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]
det = 0
for col in range(size):
submatrix = [row[:col] + row[col+1:] for row in matrix[1:]]
det += ((-1) ** col) * matrix[0][col] * determinant(submatrix)
return det
# Вычисление детерминанта параллельно
def parallel_determinant(matrix, num_processes):
size = len(matrix)
if size <= 2:
return determinant(matrix)
# Разбиение задачи по строкам на несколько потоков
chunk_size = size // num_processes
chunks = []
# Создание задач для потоков
for i in range(num_processes):
start_row = i * chunk_size
end_row = (i + 1) * chunk_size if i < num_processes - 1 else size
chunks.append((matrix[start_row:end_row], i))
with multiprocessing.Pool(processes=num_processes) as pool:
results = pool.starmap(calculate_determinant_chunk, [(matrix, chunk[0], chunk[1]) for chunk in chunks])
det = sum(results)
return det
# Вычисление детерминанта для части матрицы в одном процессе
def calculate_determinant_chunk(matrix, chunk, chunk_index):
size = len(matrix)
det = 0
for row in chunk:
for col in range(size):
submatrix = [r[:col] + r[col+1:] for r in matrix[1:]]
det += ((-1) ** (chunk_index + col)) * matrix[0][col] * determinant(submatrix)
return det
# Замер времени для параллельного вычисления детерминанта
def benchmark(size, num_processes=1):
matrix = generate_matrix(size)
start_time = time.time()
parallel_determinant(matrix, num_processes)
par_time = time.time() - start_time
return par_time
def main():
# Размеры матриц
matrix_sizes = [9, 10, 11]
# Количество потоков
num_processes_list = [1, 2, 4, 6, 8]
# Таблица с бенчмарками
print("-*" * 40)
print(f"{'Количество потоков':<20}{'|9x9 (сек.)':<20}{'|10x10 (сек.)':<20}{'|11x11 (сек.)'}")
print("-*" * 40)
for num_processes in num_processes_list:
row = f"{num_processes:<20}"
for size in matrix_sizes:
par_time = benchmark(size, num_processes)
row += f"|{par_time:.4f}".ljust(20)
print(row)
print("-*" * 40)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,18 @@
# Лабораторная работа №7 - Балансировка нагрузки в распределённых системах при помощи открытых технологий на примерах
## ПИбд-42 || Давыдов Юрий
### Задание:
Написать небольшое эссе (буквально несколько абзацев) своими словами.
Балансировка нагрузки — это важный процесс при создании распределённых систем, направленный на равномерное распределение запросов между серверами или узлами, чтобы предотвратить перегрузку отдельных частей системы. Для этого используются разные методы и алгоритмы, которые зависят от характеристик системы и её производительности.
Одним из популярных алгоритмов является "круговое распределение" (Round Robin), при котором запросы равномерно направляются на доступные серверы. Другим распространённым методом является "взвешенное распределение" (Weighted Round Robin), где запросы отправляются на серверы с учётом их вычислительных мощностей. Также используется алгоритм Least Connections, при котором запросы обрабатываются серверами с наименьшим количеством активных подключений.
Существует множество открытых технологий для балансировки нагрузки, среди которых выделяются Nginx, HAProxy и Kubernetes. Эти решения предлагают различные алгоритмы балансировки. Например, Nginx и HAProxy используют традиционные методы, а Kubernetes интегрирует балансировку в своём механизме управления контейнерами.
Для баз данных используется другой подход — репликация. Она позволяет распределять запросы между различными копиями базы данных, что способствует улучшению производительности и отказоустойчивости системы.
Реверс-прокси представляет собой сервер, который находится между клиентом и ресурсами, такими как веб-серверы. Он может кэшировать данные, сжимать трафик и фильтровать запросы, улучшая таким образом эффективность работы всей системы. Примеры таких решений — Nginx и Traefik.
Таким образом, балансировка нагрузки с использованием различных технологий и алгоритмов играет ключевую роль в оптимизации работы распределённых систем, повышая их производительность, отказоустойчивость и способность к масштабированию.

View File

@ -0,0 +1,27 @@
# Лабораторная работа №8 - Про устройство распределенных систем
## ПИбд-42 || Давыдов Юрий
### Задание:
Написать небольшое эссе (буквально несколько абзацев) своими словами.
Сложные системы, такие как социальная сеть ВКонтакте, разрабатываются в распределённой архитектуре, потому что такой подход позволяет эффективно управлять запросами, обеспечивать отказоустойчивость и масштабируемость. Каждый сервис выполняет узкоспециализированные функции, например, сервис аутентификации или сервис рекомендаций. Это важно, потому что в случае сбоя одного из сервисов остальные продолжают работать, что снижает вероятность полной остановки системы.
Системы оркестрации приложений предназначены для управления различными компонентами распределённой системы. Они упрощают разработку и обслуживание, автоматизируя процессы управления, масштабирования и обновлений. Однако они требуют от разработчиков дополнительных знаний и умений, что добавляет сложности. Примеры таких систем — Kubernetes и Docker Swarm.
Очереди обработки сообщений используются для асинхронного обмена данными между сервисами, обеспечивая их взаимодействие. Сообщения могут быть любыми данными или запросами, которые передаются между компонентами системы. Очереди помогают эффективно управлять потоком данных, предотвращая перегрузки. Примеры таких систем — RabbitMQ и Apache Kafka.
Преимущества распределённых систем:
Повышенная отказоустойчивость;
Лучшая масштабируемость;
Возможность балансировки нагрузки между серверами.
Недостатки:
Более сложная разработка по сравнению с монолитными системами;
Монолитные системы обычно требуют меньше времени на взаимодействие компонентов, так как все компоненты работают в одном процессе;
Распределённые системы требуют дополнительных организационных и ресурсных затрат для разработки и поддержки.
Внедрение параллельных вычислений в сложные распределённые системы целесообразно в случаях, когда необходимо обрабатывать независимые задачи или когда требуется высокая производительность, например, при выполнении сложных математических расчётов или обработке больших данных. Однако это не всегда оправдано, особенно когда задачи маленькие или последовательные, как, например, рекурсивное вычисление факториала числа.
Итак, распределённые системы — это важная и необходимая архитектура, которая может быть оптимальной для некоторых случаев, но не всегда. Правильный выбор подхода на этапе проектирования критичен для успеха системы.