diff --git a/davydov_yuriy_lab_1/1.mp4 b/davydov_yuriy_lab_1/1.mp4 new file mode 100644 index 0000000..27307f7 Binary files /dev/null and b/davydov_yuriy_lab_1/1.mp4 differ diff --git a/davydov_yuriy_lab_1/docker-compose.yml b/davydov_yuriy_lab_1/docker-compose.yml new file mode 100644 index 0000000..f643abb --- /dev/null +++ b/davydov_yuriy_lab_1/docker-compose.yml @@ -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: \ No newline at end of file diff --git a/davydov_yuriy_lab_1/readme.md b/davydov_yuriy_lab_1/readme.md new file mode 100644 index 0000000..ed8c2e7 --- /dev/null +++ b/davydov_yuriy_lab_1/readme.md @@ -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 +``` diff --git a/davydov_yuriy_lab_2/2.mp4 b/davydov_yuriy_lab_2/2.mp4 new file mode 100644 index 0000000..ecd8186 Binary files /dev/null and b/davydov_yuriy_lab_2/2.mp4 differ diff --git a/davydov_yuriy_lab_2/README.md b/davydov_yuriy_lab_2/README.md new file mode 100644 index 0000000..fad6a54 --- /dev/null +++ b/davydov_yuriy_lab_2/README.md @@ -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`. diff --git a/davydov_yuriy_lab_2/WorkFirst/Dockerfile b/davydov_yuriy_lab_2/WorkFirst/Dockerfile new file mode 100644 index 0000000..798cfab --- /dev/null +++ b/davydov_yuriy_lab_2/WorkFirst/Dockerfile @@ -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"] \ No newline at end of file diff --git a/davydov_yuriy_lab_2/WorkFirst/main.py b/davydov_yuriy_lab_2/WorkFirst/main.py new file mode 100644 index 0000000..ee3211b --- /dev/null +++ b/davydov_yuriy_lab_2/WorkFirst/main.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_2/WorkSecond/Dockerfile b/davydov_yuriy_lab_2/WorkSecond/Dockerfile new file mode 100644 index 0000000..798cfab --- /dev/null +++ b/davydov_yuriy_lab_2/WorkSecond/Dockerfile @@ -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"] \ No newline at end of file diff --git a/davydov_yuriy_lab_2/WorkSecond/main.py b/davydov_yuriy_lab_2/WorkSecond/main.py new file mode 100644 index 0000000..dc65c59 --- /dev/null +++ b/davydov_yuriy_lab_2/WorkSecond/main.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_2/docker-compose.yml b/davydov_yuriy_lab_2/docker-compose.yml new file mode 100644 index 0000000..f2192d8 --- /dev/null +++ b/davydov_yuriy_lab_2/docker-compose.yml @@ -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 \ No newline at end of file diff --git a/davydov_yuriy_lab_3/3.mp4 b/davydov_yuriy_lab_3/3.mp4 new file mode 100644 index 0000000..8c6b3e6 Binary files /dev/null and b/davydov_yuriy_lab_3/3.mp4 differ diff --git a/davydov_yuriy_lab_3/README.md b/davydov_yuriy_lab_3/README.md new file mode 100644 index 0000000..b304004 --- /dev/null +++ b/davydov_yuriy_lab_3/README.md @@ -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. diff --git a/davydov_yuriy_lab_3/docker-compose.yaml b/davydov_yuriy_lab_3/docker-compose.yaml new file mode 100644 index 0000000..6724b9e --- /dev/null +++ b/davydov_yuriy_lab_3/docker-compose.yaml @@ -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 \ No newline at end of file diff --git a/davydov_yuriy_lab_3/faculty_service/Dockerfile b/davydov_yuriy_lab_3/faculty_service/Dockerfile new file mode 100644 index 0000000..b97a53a --- /dev/null +++ b/davydov_yuriy_lab_3/faculty_service/Dockerfile @@ -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"] \ No newline at end of file diff --git a/davydov_yuriy_lab_3/faculty_service/faculty_service.py b/davydov_yuriy_lab_3/faculty_service/faculty_service.py new file mode 100644 index 0000000..ff89a64 --- /dev/null +++ b/davydov_yuriy_lab_3/faculty_service/faculty_service.py @@ -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('/', 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/', 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/', 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('/', 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('/', 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) diff --git a/davydov_yuriy_lab_3/nginx.conf b/davydov_yuriy_lab_3/nginx.conf new file mode 100644 index 0000000..75c0926 --- /dev/null +++ b/davydov_yuriy_lab_3/nginx.conf @@ -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; + } + } +} \ No newline at end of file diff --git a/davydov_yuriy_lab_3/requirements.txt b/davydov_yuriy_lab_3/requirements.txt new file mode 100644 index 0000000..41917d8 Binary files /dev/null and b/davydov_yuriy_lab_3/requirements.txt differ diff --git a/davydov_yuriy_lab_3/speciality_service/Dockerfile b/davydov_yuriy_lab_3/speciality_service/Dockerfile new file mode 100644 index 0000000..a84ff78 --- /dev/null +++ b/davydov_yuriy_lab_3/speciality_service/Dockerfile @@ -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"] \ No newline at end of file diff --git a/davydov_yuriy_lab_3/speciality_service/speciality_service.py b/davydov_yuriy_lab_3/speciality_service/speciality_service.py new file mode 100644 index 0000000..2b72e80 --- /dev/null +++ b/davydov_yuriy_lab_3/speciality_service/speciality_service.py @@ -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/', 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('/', 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/', 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('/', 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('/', 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) diff --git a/davydov_yuriy_lab_4/Consumer_1.py b/davydov_yuriy_lab_4/Consumer_1.py new file mode 100644 index 0000000..699d7c8 --- /dev/null +++ b/davydov_yuriy_lab_4/Consumer_1.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_4/Consumer_2.py b/davydov_yuriy_lab_4/Consumer_2.py new file mode 100644 index 0000000..a0e08a1 --- /dev/null +++ b/davydov_yuriy_lab_4/Consumer_2.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_4/Publisher.py b/davydov_yuriy_lab_4/Publisher.py new file mode 100644 index 0000000..de4b2c7 --- /dev/null +++ b/davydov_yuriy_lab_4/Publisher.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_4/README.md b/davydov_yuriy_lab_4/README.md new file mode 100644 index 0000000..0fe239a --- /dev/null +++ b/davydov_yuriy_lab_4/README.md @@ -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 запущено, тем медленнее заполняется очередь. Это потому, что каждый из экземпляров занимается обработкой сообщений из очереди. С увеличением числа экземпляров можно достичь момента, когда очередь перестанет заполняться. diff --git a/davydov_yuriy_lab_4/lesson_1/receive.py b/davydov_yuriy_lab_4/lesson_1/receive.py new file mode 100644 index 0000000..f118e0a --- /dev/null +++ b/davydov_yuriy_lab_4/lesson_1/receive.py @@ -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) \ No newline at end of file diff --git a/davydov_yuriy_lab_4/lesson_1/send.py b/davydov_yuriy_lab_4/lesson_1/send.py new file mode 100644 index 0000000..41cfff2 --- /dev/null +++ b/davydov_yuriy_lab_4/lesson_1/send.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_4/lesson_2/new_task.py b/davydov_yuriy_lab_4/lesson_2/new_task.py new file mode 100644 index 0000000..a2444e0 --- /dev/null +++ b/davydov_yuriy_lab_4/lesson_2/new_task.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_4/lesson_2/worker.py b/davydov_yuriy_lab_4/lesson_2/worker.py new file mode 100644 index 0000000..8f8ab64 --- /dev/null +++ b/davydov_yuriy_lab_4/lesson_2/worker.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_4/lesson_3/emit_log.py b/davydov_yuriy_lab_4/lesson_3/emit_log.py new file mode 100644 index 0000000..45b6989 --- /dev/null +++ b/davydov_yuriy_lab_4/lesson_3/emit_log.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_4/lesson_3/receive_logs.py b/davydov_yuriy_lab_4/lesson_3/receive_logs.py new file mode 100644 index 0000000..60d881d --- /dev/null +++ b/davydov_yuriy_lab_4/lesson_3/receive_logs.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_5/5.mp4 b/davydov_yuriy_lab_5/5.mp4 new file mode 100644 index 0000000..8d86fd4 Binary files /dev/null and b/davydov_yuriy_lab_5/5.mp4 differ diff --git a/davydov_yuriy_lab_5/README.md b/davydov_yuriy_lab_5/README.md new file mode 100644 index 0000000..f544ad6 --- /dev/null +++ b/davydov_yuriy_lab_5/README.md @@ -0,0 +1,11 @@ +# Лабораторная работа №5 - Параллельное перемножение матриц + +## ПИбд-42 || Давыдов Юрий + +### Цель лабораторной работы + +Изучение принципов работы праллельных вычислений, когда они оправданы, а когда нет. + +### Описание: + +Был реализован механизм параллельного перемножения матриц 100x100, 300x300 и 500x500 с возможностью задания потоков, в том числе и 1 (последовательное перемножение). Были сделаны замеры времени для каждого вычисления, проведен анализ и сделаны выводы. diff --git a/davydov_yuriy_lab_5/main.py b/davydov_yuriy_lab_5/main.py new file mode 100644 index 0000000..2172c2f --- /dev/null +++ b/davydov_yuriy_lab_5/main.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_6/6.mp4 b/davydov_yuriy_lab_6/6.mp4 new file mode 100644 index 0000000..97fc00a Binary files /dev/null and b/davydov_yuriy_lab_6/6.mp4 differ diff --git a/davydov_yuriy_lab_6/README.md b/davydov_yuriy_lab_6/README.md new file mode 100644 index 0000000..01a7d24 --- /dev/null +++ b/davydov_yuriy_lab_6/README.md @@ -0,0 +1,22 @@ +# Лабораторная работа №6 - Параллельный поиск значения детерминанта матрицы + +## ПИбд-42 || Давыдов Юрий + +### Цель лабораторной работы + +Изучение принципов работы праллельных вычислений, когда они оправданы, а когда нет. + +### Описание: + +Был реализован механизм для параллельного вычисления детерминанта матриц с возможностью задания потоков, в том числе и 1 (обычное вычисление). Был применен на матрицах размером 9x9, 10x10 и 11x11. Были сделаны замеры времени для каждого вычисления, проведен анализ и сделаны выводы. + +### Результаты: + +![Изображение 1](./results.png) + +### Выводы: + +При параллельном поиске детерминанта мы нацелены уменьшить временные затраты за счет увеличения числа потоков. Это действительно дает свои плоды, но есть некоторые нюансы. +Из результатов видно, что для вычисления детерминанта матрицы в одном потоке потребовалось 322 секунды, тогда как в 8 потоках время составило 142 секунды, а это значит, что мы произвели вычисления более чем вдвое быстрее. +При этом при применении такого подхода к малым вычислениям, мы наоборот можем просесть по затратам, т. к. для управления многопоточностью тоже требуются ресурсы, поэтому определять целесообразность разбиения задачи на потоки следует исходя из ее объема. +Следующий момент - это определение оптимального числа потоков, не всегда больше = лучше, ведь на менеджмент новых потоков также придется тратить ресурсы. diff --git a/davydov_yuriy_lab_6/main.py b/davydov_yuriy_lab_6/main.py new file mode 100644 index 0000000..a4952a5 --- /dev/null +++ b/davydov_yuriy_lab_6/main.py @@ -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() \ No newline at end of file diff --git a/davydov_yuriy_lab_7/README.md b/davydov_yuriy_lab_7/README.md new file mode 100644 index 0000000..0d79379 --- /dev/null +++ b/davydov_yuriy_lab_7/README.md @@ -0,0 +1,18 @@ +# Лабораторная работа №7 - Балансировка нагрузки в распределённых системах при помощи открытых технологий на примерах + +## ПИбд-42 || Давыдов Юрий + +### Задание: + +Написать небольшое эссе (буквально несколько абзацев) своими словами. +Балансировка нагрузки — это важный процесс при создании распределённых систем, направленный на равномерное распределение запросов между серверами или узлами, чтобы предотвратить перегрузку отдельных частей системы. Для этого используются разные методы и алгоритмы, которые зависят от характеристик системы и её производительности. + +Одним из популярных алгоритмов является "круговое распределение" (Round Robin), при котором запросы равномерно направляются на доступные серверы. Другим распространённым методом является "взвешенное распределение" (Weighted Round Robin), где запросы отправляются на серверы с учётом их вычислительных мощностей. Также используется алгоритм Least Connections, при котором запросы обрабатываются серверами с наименьшим количеством активных подключений. + +Существует множество открытых технологий для балансировки нагрузки, среди которых выделяются Nginx, HAProxy и Kubernetes. Эти решения предлагают различные алгоритмы балансировки. Например, Nginx и HAProxy используют традиционные методы, а Kubernetes интегрирует балансировку в своём механизме управления контейнерами. + +Для баз данных используется другой подход — репликация. Она позволяет распределять запросы между различными копиями базы данных, что способствует улучшению производительности и отказоустойчивости системы. + +Реверс-прокси представляет собой сервер, который находится между клиентом и ресурсами, такими как веб-серверы. Он может кэшировать данные, сжимать трафик и фильтровать запросы, улучшая таким образом эффективность работы всей системы. Примеры таких решений — Nginx и Traefik. + +Таким образом, балансировка нагрузки с использованием различных технологий и алгоритмов играет ключевую роль в оптимизации работы распределённых систем, повышая их производительность, отказоустойчивость и способность к масштабированию. diff --git a/davydov_yuriy_lab_8/README.md b/davydov_yuriy_lab_8/README.md new file mode 100644 index 0000000..9afec31 --- /dev/null +++ b/davydov_yuriy_lab_8/README.md @@ -0,0 +1,27 @@ +# Лабораторная работа №8 - Про устройство распределенных систем + +## ПИбд-42 || Давыдов Юрий + +### Задание: + +Написать небольшое эссе (буквально несколько абзацев) своими словами. + +Сложные системы, такие как социальная сеть ВКонтакте, разрабатываются в распределённой архитектуре, потому что такой подход позволяет эффективно управлять запросами, обеспечивать отказоустойчивость и масштабируемость. Каждый сервис выполняет узкоспециализированные функции, например, сервис аутентификации или сервис рекомендаций. Это важно, потому что в случае сбоя одного из сервисов остальные продолжают работать, что снижает вероятность полной остановки системы. + +Системы оркестрации приложений предназначены для управления различными компонентами распределённой системы. Они упрощают разработку и обслуживание, автоматизируя процессы управления, масштабирования и обновлений. Однако они требуют от разработчиков дополнительных знаний и умений, что добавляет сложности. Примеры таких систем — Kubernetes и Docker Swarm. + +Очереди обработки сообщений используются для асинхронного обмена данными между сервисами, обеспечивая их взаимодействие. Сообщения могут быть любыми данными или запросами, которые передаются между компонентами системы. Очереди помогают эффективно управлять потоком данных, предотвращая перегрузки. Примеры таких систем — RabbitMQ и Apache Kafka. + +Преимущества распределённых систем: + +Повышенная отказоустойчивость; +Лучшая масштабируемость; +Возможность балансировки нагрузки между серверами. +Недостатки: + +Более сложная разработка по сравнению с монолитными системами; +Монолитные системы обычно требуют меньше времени на взаимодействие компонентов, так как все компоненты работают в одном процессе; +Распределённые системы требуют дополнительных организационных и ресурсных затрат для разработки и поддержки. +Внедрение параллельных вычислений в сложные распределённые системы целесообразно в случаях, когда необходимо обрабатывать независимые задачи или когда требуется высокая производительность, например, при выполнении сложных математических расчётов или обработке больших данных. Однако это не всегда оправдано, особенно когда задачи маленькие или последовательные, как, например, рекурсивное вычисление факториала числа. + +Итак, распределённые системы — это важная и необходимая архитектура, которая может быть оптимальной для некоторых случаев, но не всегда. Правильный выбор подхода на этапе проектирования критичен для успеха системы.