Labs
This commit is contained in:
parent
bc087de470
commit
9ddb3d475d
BIN
davydov_yuriy_lab_1/1.mp4
Normal file
BIN
davydov_yuriy_lab_1/1.mp4
Normal file
Binary file not shown.
45
davydov_yuriy_lab_1/docker-compose.yml
Normal file
45
davydov_yuriy_lab_1/docker-compose.yml
Normal 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:
|
38
davydov_yuriy_lab_1/readme.md
Normal file
38
davydov_yuriy_lab_1/readme.md
Normal 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
BIN
davydov_yuriy_lab_2/2.mp4
Normal file
Binary file not shown.
91
davydov_yuriy_lab_2/README.md
Normal file
91
davydov_yuriy_lab_2/README.md
Normal 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`.
|
16
davydov_yuriy_lab_2/WorkFirst/Dockerfile
Normal file
16
davydov_yuriy_lab_2/WorkFirst/Dockerfile
Normal 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"]
|
81
davydov_yuriy_lab_2/WorkFirst/main.py
Normal file
81
davydov_yuriy_lab_2/WorkFirst/main.py
Normal 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()
|
16
davydov_yuriy_lab_2/WorkSecond/Dockerfile
Normal file
16
davydov_yuriy_lab_2/WorkSecond/Dockerfile
Normal 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"]
|
60
davydov_yuriy_lab_2/WorkSecond/main.py
Normal file
60
davydov_yuriy_lab_2/WorkSecond/main.py
Normal 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()
|
21
davydov_yuriy_lab_2/docker-compose.yml
Normal file
21
davydov_yuriy_lab_2/docker-compose.yml
Normal 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
BIN
davydov_yuriy_lab_3/3.mp4
Normal file
Binary file not shown.
39
davydov_yuriy_lab_3/README.md
Normal file
39
davydov_yuriy_lab_3/README.md
Normal 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.
|
27
davydov_yuriy_lab_3/docker-compose.yaml
Normal file
27
davydov_yuriy_lab_3/docker-compose.yaml
Normal 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
|
17
davydov_yuriy_lab_3/faculty_service/Dockerfile
Normal file
17
davydov_yuriy_lab_3/faculty_service/Dockerfile
Normal 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"]
|
129
davydov_yuriy_lab_3/faculty_service/faculty_service.py
Normal file
129
davydov_yuriy_lab_3/faculty_service/faculty_service.py
Normal 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)
|
26
davydov_yuriy_lab_3/nginx.conf
Normal file
26
davydov_yuriy_lab_3/nginx.conf
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
davydov_yuriy_lab_3/requirements.txt
Normal file
BIN
davydov_yuriy_lab_3/requirements.txt
Normal file
Binary file not shown.
17
davydov_yuriy_lab_3/speciality_service/Dockerfile
Normal file
17
davydov_yuriy_lab_3/speciality_service/Dockerfile
Normal 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"]
|
161
davydov_yuriy_lab_3/speciality_service/speciality_service.py
Normal file
161
davydov_yuriy_lab_3/speciality_service/speciality_service.py
Normal 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)
|
25
davydov_yuriy_lab_4/Consumer_1.py
Normal file
25
davydov_yuriy_lab_4/Consumer_1.py
Normal 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()
|
22
davydov_yuriy_lab_4/Consumer_2.py
Normal file
22
davydov_yuriy_lab_4/Consumer_2.py
Normal 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()
|
18
davydov_yuriy_lab_4/Publisher.py
Normal file
18
davydov_yuriy_lab_4/Publisher.py
Normal 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()
|
44
davydov_yuriy_lab_4/README.md
Normal file
44
davydov_yuriy_lab_4/README.md
Normal 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 запущено, тем медленнее заполняется очередь. Это потому, что каждый из экземпляров занимается обработкой сообщений из очереди. С увеличением числа экземпляров можно достичь момента, когда очередь перестанет заполняться.
|
25
davydov_yuriy_lab_4/lesson_1/receive.py
Normal file
25
davydov_yuriy_lab_4/lesson_1/receive.py
Normal 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)
|
11
davydov_yuriy_lab_4/lesson_1/send.py
Normal file
11
davydov_yuriy_lab_4/lesson_1/send.py
Normal 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()
|
19
davydov_yuriy_lab_4/lesson_2/new_task.py
Normal file
19
davydov_yuriy_lab_4/lesson_2/new_task.py
Normal 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()
|
22
davydov_yuriy_lab_4/lesson_2/worker.py
Normal file
22
davydov_yuriy_lab_4/lesson_2/worker.py
Normal 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()
|
13
davydov_yuriy_lab_4/lesson_3/emit_log.py
Normal file
13
davydov_yuriy_lab_4/lesson_3/emit_log.py
Normal 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()
|
22
davydov_yuriy_lab_4/lesson_3/receive_logs.py
Normal file
22
davydov_yuriy_lab_4/lesson_3/receive_logs.py
Normal 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
BIN
davydov_yuriy_lab_5/5.mp4
Normal file
Binary file not shown.
11
davydov_yuriy_lab_5/README.md
Normal file
11
davydov_yuriy_lab_5/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Лабораторная работа №5 - Параллельное перемножение матриц
|
||||||
|
|
||||||
|
## ПИбд-42 || Давыдов Юрий
|
||||||
|
|
||||||
|
### Цель лабораторной работы
|
||||||
|
|
||||||
|
Изучение принципов работы праллельных вычислений, когда они оправданы, а когда нет.
|
||||||
|
|
||||||
|
### Описание:
|
||||||
|
|
||||||
|
Был реализован механизм параллельного перемножения матриц 100x100, 300x300 и 500x500 с возможностью задания потоков, в том числе и 1 (последовательное перемножение). Были сделаны замеры времени для каждого вычисления, проведен анализ и сделаны выводы.
|
58
davydov_yuriy_lab_5/main.py
Normal file
58
davydov_yuriy_lab_5/main.py
Normal 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
BIN
davydov_yuriy_lab_6/6.mp4
Normal file
Binary file not shown.
22
davydov_yuriy_lab_6/README.md
Normal file
22
davydov_yuriy_lab_6/README.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Лабораторная работа №6 - Параллельный поиск значения детерминанта матрицы
|
||||||
|
|
||||||
|
## ПИбд-42 || Давыдов Юрий
|
||||||
|
|
||||||
|
### Цель лабораторной работы
|
||||||
|
|
||||||
|
Изучение принципов работы праллельных вычислений, когда они оправданы, а когда нет.
|
||||||
|
|
||||||
|
### Описание:
|
||||||
|
|
||||||
|
Был реализован механизм для параллельного вычисления детерминанта матриц с возможностью задания потоков, в том числе и 1 (обычное вычисление). Был применен на матрицах размером 9x9, 10x10 и 11x11. Были сделаны замеры времени для каждого вычисления, проведен анализ и сделаны выводы.
|
||||||
|
|
||||||
|
### Результаты:
|
||||||
|
|
||||||
|
![Изображение 1](./results.png)
|
||||||
|
|
||||||
|
### Выводы:
|
||||||
|
|
||||||
|
При параллельном поиске детерминанта мы нацелены уменьшить временные затраты за счет увеличения числа потоков. Это действительно дает свои плоды, но есть некоторые нюансы.
|
||||||
|
Из результатов видно, что для вычисления детерминанта матрицы в одном потоке потребовалось 322 секунды, тогда как в 8 потоках время составило 142 секунды, а это значит, что мы произвели вычисления более чем вдвое быстрее.
|
||||||
|
При этом при применении такого подхода к малым вычислениям, мы наоборот можем просесть по затратам, т. к. для управления многопоточностью тоже требуются ресурсы, поэтому определять целесообразность разбиения задачи на потоки следует исходя из ее объема.
|
||||||
|
Следующий момент - это определение оптимального числа потоков, не всегда больше = лучше, ведь на менеджмент новых потоков также придется тратить ресурсы.
|
84
davydov_yuriy_lab_6/main.py
Normal file
84
davydov_yuriy_lab_6/main.py
Normal 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()
|
18
davydov_yuriy_lab_7/README.md
Normal file
18
davydov_yuriy_lab_7/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Лабораторная работа №7 - Балансировка нагрузки в распределённых системах при помощи открытых технологий на примерах
|
||||||
|
|
||||||
|
## ПИбд-42 || Давыдов Юрий
|
||||||
|
|
||||||
|
### Задание:
|
||||||
|
|
||||||
|
Написать небольшое эссе (буквально несколько абзацев) своими словами.
|
||||||
|
Балансировка нагрузки — это важный процесс при создании распределённых систем, направленный на равномерное распределение запросов между серверами или узлами, чтобы предотвратить перегрузку отдельных частей системы. Для этого используются разные методы и алгоритмы, которые зависят от характеристик системы и её производительности.
|
||||||
|
|
||||||
|
Одним из популярных алгоритмов является "круговое распределение" (Round Robin), при котором запросы равномерно направляются на доступные серверы. Другим распространённым методом является "взвешенное распределение" (Weighted Round Robin), где запросы отправляются на серверы с учётом их вычислительных мощностей. Также используется алгоритм Least Connections, при котором запросы обрабатываются серверами с наименьшим количеством активных подключений.
|
||||||
|
|
||||||
|
Существует множество открытых технологий для балансировки нагрузки, среди которых выделяются Nginx, HAProxy и Kubernetes. Эти решения предлагают различные алгоритмы балансировки. Например, Nginx и HAProxy используют традиционные методы, а Kubernetes интегрирует балансировку в своём механизме управления контейнерами.
|
||||||
|
|
||||||
|
Для баз данных используется другой подход — репликация. Она позволяет распределять запросы между различными копиями базы данных, что способствует улучшению производительности и отказоустойчивости системы.
|
||||||
|
|
||||||
|
Реверс-прокси представляет собой сервер, который находится между клиентом и ресурсами, такими как веб-серверы. Он может кэшировать данные, сжимать трафик и фильтровать запросы, улучшая таким образом эффективность работы всей системы. Примеры таких решений — Nginx и Traefik.
|
||||||
|
|
||||||
|
Таким образом, балансировка нагрузки с использованием различных технологий и алгоритмов играет ключевую роль в оптимизации работы распределённых систем, повышая их производительность, отказоустойчивость и способность к масштабированию.
|
27
davydov_yuriy_lab_8/README.md
Normal file
27
davydov_yuriy_lab_8/README.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Лабораторная работа №8 - Про устройство распределенных систем
|
||||||
|
|
||||||
|
## ПИбд-42 || Давыдов Юрий
|
||||||
|
|
||||||
|
### Задание:
|
||||||
|
|
||||||
|
Написать небольшое эссе (буквально несколько абзацев) своими словами.
|
||||||
|
|
||||||
|
Сложные системы, такие как социальная сеть ВКонтакте, разрабатываются в распределённой архитектуре, потому что такой подход позволяет эффективно управлять запросами, обеспечивать отказоустойчивость и масштабируемость. Каждый сервис выполняет узкоспециализированные функции, например, сервис аутентификации или сервис рекомендаций. Это важно, потому что в случае сбоя одного из сервисов остальные продолжают работать, что снижает вероятность полной остановки системы.
|
||||||
|
|
||||||
|
Системы оркестрации приложений предназначены для управления различными компонентами распределённой системы. Они упрощают разработку и обслуживание, автоматизируя процессы управления, масштабирования и обновлений. Однако они требуют от разработчиков дополнительных знаний и умений, что добавляет сложности. Примеры таких систем — Kubernetes и Docker Swarm.
|
||||||
|
|
||||||
|
Очереди обработки сообщений используются для асинхронного обмена данными между сервисами, обеспечивая их взаимодействие. Сообщения могут быть любыми данными или запросами, которые передаются между компонентами системы. Очереди помогают эффективно управлять потоком данных, предотвращая перегрузки. Примеры таких систем — RabbitMQ и Apache Kafka.
|
||||||
|
|
||||||
|
Преимущества распределённых систем:
|
||||||
|
|
||||||
|
Повышенная отказоустойчивость;
|
||||||
|
Лучшая масштабируемость;
|
||||||
|
Возможность балансировки нагрузки между серверами.
|
||||||
|
Недостатки:
|
||||||
|
|
||||||
|
Более сложная разработка по сравнению с монолитными системами;
|
||||||
|
Монолитные системы обычно требуют меньше времени на взаимодействие компонентов, так как все компоненты работают в одном процессе;
|
||||||
|
Распределённые системы требуют дополнительных организационных и ресурсных затрат для разработки и поддержки.
|
||||||
|
Внедрение параллельных вычислений в сложные распределённые системы целесообразно в случаях, когда необходимо обрабатывать независимые задачи или когда требуется высокая производительность, например, при выполнении сложных математических расчётов или обработке больших данных. Однако это не всегда оправдано, особенно когда задачи маленькие или последовательные, как, например, рекурсивное вычисление факториала числа.
|
||||||
|
|
||||||
|
Итак, распределённые системы — это важная и необходимая архитектура, которая может быть оптимальной для некоторых случаев, но не всегда. Правильный выбор подхода на этапе проектирования критичен для успеха системы.
|
Loading…
Reference in New Issue
Block a user