diff --git a/khaliullov_ramil_lab_3/MasterService/.gitignore b/khaliullov_ramil_lab_3/MasterService/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/khaliullov_ramil_lab_3/MasterService/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/khaliullov_ramil_lab_3/MasterService/src/main/java/org/example/App.java b/khaliullov_ramil_lab_3/MasterService/src/main/java/org/example/App.java new file mode 100644 index 0000000..b630ea1 --- /dev/null +++ b/khaliullov_ramil_lab_3/MasterService/src/main/java/org/example/App.java @@ -0,0 +1,11 @@ +package org.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} \ No newline at end of file diff --git a/khaliullov_ramil_lab_3/MasterService/src/main/java/org/example/AppConfig.java b/khaliullov_ramil_lab_3/MasterService/src/main/java/org/example/AppConfig.java new file mode 100644 index 0000000..fdf26e1 --- /dev/null +++ b/khaliullov_ramil_lab_3/MasterService/src/main/java/org/example/AppConfig.java @@ -0,0 +1,14 @@ +package org.example; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class AppConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/khaliullov_ramil_lab_3/MasterService/src/main/java/org/example/MasterController.java b/khaliullov_ramil_lab_3/MasterService/src/main/java/org/example/MasterController.java new file mode 100644 index 0000000..d305b3f --- /dev/null +++ b/khaliullov_ramil_lab_3/MasterService/src/main/java/org/example/MasterController.java @@ -0,0 +1,184 @@ +package org.example; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; + +import java.text.DecimalFormat; +import java.util.Random; + +@RestController +@RequestMapping("/master") +public class MasterController { + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private ApplicationContext context; + + private static final String WORKER2_URL = "http://192.168.28.104:8080/worker/divide"; + private static final String WORKER1_URL = "http://192.168.28.106:8080/worker/divide"; + + + // Максимальное количество элементов для логирования + private static final int MAX_LOG_ELEMENTS = 8; + + @GetMapping("/generate-matrix") + public ResponseEntity generateAndSendMatrix() { + int[][] matrix = generateMatrix(); // Генерация целочисленной матрицы + double[][] matrixDouble = convertToDoubleMatrix(matrix); // Преобразуем в double для вывода + int size = 8; // хотел добавить пользовательский размер для вывода матрицы (но пофиг) + logMatrixPreview(matrixDouble, "Generated matrix"); // Логируем в формате double + int mid = matrix[0].length / 2; // Половина количества столбцов + int[][] matrixPart1 = copyColumns(matrix, 0, mid); + int[][] matrixPart2 = copyColumns(matrix, mid, matrix[0].length); + + long startTime = System.currentTimeMillis(); + + ResponseEntity response1 = restTemplate.postForEntity(WORKER1_URL, convertToDoubleMatrix(matrixPart1), double[][].class); + ResponseEntity response2 = restTemplate.postForEntity(WORKER2_URL, convertToDoubleMatrix(matrixPart2), double[][].class); + + if (response1.getStatusCode().is2xxSuccessful() && response2.getStatusCode().is2xxSuccessful()) { + double[][] sortedMatrix1 = response1.getBody(); + double[][] sortedMatrix2 = response2.getBody(); + + double[][] mergedMatrix = mergeSortedMatrices(sortedMatrix1, sortedMatrix2); + + logMatrixPreview(mergedMatrix, "Final Sorted Matrix"); + + long endTime = System.currentTimeMillis(); + long elapsedTime = endTime - startTime; + System.out.println("Total sorting time: " + elapsedTime + " ms"); + + // Извлекаем нужную часть матрицы для клиента + double[][] subMatrix = extractSubMatrix(mergedMatrix, size); + + // Возвращаем её клиенту + return ResponseEntity.ok(subMatrix); + + } + return ResponseEntity.status(500).body(null); + } + + // Метод для получения части матрицы + private double[][] extractSubMatrix(double[][] matrix, int size) { + int rows = Math.min(size, matrix.length); + int cols = Math.min(size, matrix[0].length); + double[][] subMatrix = new double[rows][cols]; + + for (int i = 0; i < rows; i++) { + System.arraycopy(matrix[i], 0, subMatrix[i], 0, cols); + } + return subMatrix; + } + + private int[][] copyColumns(int[][] matrix, int startCol, int endCol) { + int[][] subMatrix = new int[matrix.length][endCol - startCol]; + + for (int i = 0; i < matrix.length; i++) { + System.arraycopy(matrix[i], startCol, subMatrix[i], 0, endCol - startCol); + } + return subMatrix; + } + + // Метод для логирования части матрицы с ограничением по числу элементов + private void logMatrixPreview(double[][] matrix, String message) { + StringBuilder preview = new StringBuilder(message + ": \n"); + + int elementCount = 0; + DecimalFormat df = new DecimalFormat("#.##"); // Форматируем до 2 знаков после запятой + + outerLoop: + for (int i = 0; i < Math.min(8, matrix.length); i++) { // Логируем только 8 строк + preview.append("["); + for (int j = 0; j < Math.min(8, matrix[i].length); j++) { // Логируем только 8 столбцов + preview.append(df.format(matrix[i][j])).append(j < Math.min(8, matrix[i].length) - 1 ? ", " : ""); + elementCount++; + + if (elementCount >= 64) { // 8x8 = 64 элемента + break outerLoop; + } + } + preview.append("]"); + if (elementCount < 64) { + preview.append("\n"); + } + } + + System.out.println(preview.toString()); // Можно использовать логирование через SLF4J, если нужно + } + + private double[][] mergeSortedMatrices(double[][] sortedMatrix1, double[][] sortedMatrix2) { + int rows = sortedMatrix1.length; + int cols = sortedMatrix1[0].length + sortedMatrix2[0].length; + double[][] mergedMatrix = new double[rows][cols]; + + // Слияние по строкам + for (int i = 0; i < rows; i++) { + double[] mergedRow = new double[cols]; + int index1 = 0, index2 = 0, mergedIndex = 0; + + while (index1 < sortedMatrix1[i].length && index2 < sortedMatrix2[i].length) { + if (sortedMatrix1[i][index1] < sortedMatrix2[i][index2]) { + mergedRow[mergedIndex++] = sortedMatrix1[i][index1++]; + } else { + mergedRow[mergedIndex++] = sortedMatrix2[i][index2++]; + } + } + + // Добавляем оставшиеся элементы из первой части + while (index1 < sortedMatrix1[i].length) { + mergedRow[mergedIndex++] = sortedMatrix1[i][index1++]; + } + + // Добавляем оставшиеся элементы из второй части + while (index2 < sortedMatrix2[i].length) { + mergedRow[mergedIndex++] = sortedMatrix2[i][index2++]; + } + + mergedMatrix[i] = mergedRow; + } + + // Применяем форматирование для чисел в матрице + DecimalFormat df = new DecimalFormat("#.##"); // Форматируем до 2 знаков после запятой + for (int i = 0; i < mergedMatrix.length; i++) { + for (int j = 0; j < mergedMatrix[i].length; j++) { + mergedMatrix[i][j] = Double.parseDouble(df.format(mergedMatrix[i][j])); // Округляем значение + } + } + + return mergedMatrix; + } + + + private int[][] generateMatrix() { + int rows = 1000; + int cols = 1000; + int[][] matrix = new int[rows][cols]; + Random rand = new Random(); + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + matrix[i][j] = rand.nextInt(100); // Случайные числа от 0 до 99 + } + } + return matrix; + } + + // Метод для преобразования целочисленной матрицы в double[][] для вывода + private double[][] convertToDoubleMatrix(int[][] matrix) { + int rows = matrix.length; + int cols = matrix[0].length; + double[][] doubleMatrix = new double[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + doubleMatrix[i][j] = matrix[i][j]; // Преобразуем каждый элемент в double + } + } + return doubleMatrix; + } +} diff --git a/khaliullov_ramil_lab_3/MasterService/src/main/resources/application.properties b/khaliullov_ramil_lab_3/MasterService/src/main/resources/application.properties new file mode 100644 index 0000000..019559f --- /dev/null +++ b/khaliullov_ramil_lab_3/MasterService/src/main/resources/application.properties @@ -0,0 +1,2 @@ +server.port=8081 +spring.codec.max-in-memory-size=10MB diff --git a/khaliullov_ramil_lab_3/README.md b/khaliullov_ramil_lab_3/README.md new file mode 100644 index 0000000..92d93c0 --- /dev/null +++ b/khaliullov_ramil_lab_3/README.md @@ -0,0 +1,123 @@ + ## Лабораторная работа №3 + +### Разработка распределенного приложения с использованием фреймворка Spring Boot + +#### Описание +Разработано параллельное распределенное приложение. Приложение использует сервис-ориентированную архитектуру и запускается в LXC-контейнерах. + +### Вариант +1 Разделить элементы матрицы на наибольший элемент. + +#### Архитектура +- **Сервис-управленец (Master)** + - Генерирует матрицу по GET-запросу. + - Делит элементы матрицы на максимальный элемент. + - Отправляет части матрицы двум сервисам-работникам. + - Получает нормализованные части от работников и объединяет их. + - Возвращает результат пользователю в формате JSON. + +- **Сервис-работник (Worker)** + - Получает часть матрицы. + - Делит элементы матрицы на максимальный элемент. + - Возвращает нормализованную матрицу. + +Сервисы работают на трех контейнерах: в одном запущен мастер, во втором и третьем - работник. + +### Какие технологии использовали: + +- **Spring Boot** — для создания REST API сервиса. +- **RestTemplate** — для отправки HTTP-запросов между сервисами. +- **CompletableFuture** — асинхронное программирование. + +#### Запуск приложения +1. **Сборка jar-файлов** + ```bash + mvn clean package + ``` + Полученные файлы: + - `master-service-1.0.jar` + - `worker-service-1.0.jar` + +2. **Запуск сервисов** + ```bash + java -jar master-service-1.0.jar + java -jar worker-service-1.0.jar + ``` + +3. **Запрос к мастеру** + ```bash + curl http://192.168.28.105:8081/master/generate-matrix + ``` + +#### Выходные данные +Вывод master-service: +```powershell +Generated matrix: +[18, 12, 99, 62, 8, 72, 9, 97] +[71, 16, 73, 61, 92, 51, 1, 2] +[74, 84, 44, 70, 35, 66, 99, 15] +[71, 78, 14, 73, 71, 20, 27, 17] +[53, 76, 43, 9, 31, 77, 79, 48] +[64, 83, 83, 37, 51, 94, 87, 50] +[59, 57, 39, 37, 0, 87, 92, 23] +[25, 73, 39, 33, 15, 98, 0, 67 +Final Sorted Matrix: +[0.18, 0.12, 0.45, 0.55, 0.8, 0.61, 0.23, 0.08] +[0.27, 0.16, 0.72, 0.16, 0.74, 0.62, 0.93, 0.52] +[0.31, 0.75, 0.85, 0.44, 0.71, 0.35, 0.67, 0.95] +[0.38, 0.45, 0.02, 0.04, 0.72, 0.79, 0.14, 0.74] +[0.24, 0.06, 0.47, 0.54, 0.77, 0.43, 0.09, 0.31] +[0.65, 0.84, 0.84, 0.37, 0.52, 0.91, 0.91, 0.95] +[0.19, 0.29, 0.49, 0.18, 0.38, 0.6, 0.58, 0.39] +[0.25, 0.74, 0.39, 0.33, 0.15, 0.9, 0.07, 0.76 +Total sorting time: 6326 ms +``` +Вывод worker-service(1): +```powershell +2025-03-24T08:22:40.109Z INFO 291 --- [nio-8080-exec-1] org.example.WorkerController : Received request to divide matrix elements by max value +2025-03-24T08:22:40.120Z INFO 291 --- [nio-8080-exec-1] org.example.WorkerController : Received matrix: +[18.00, 12.00, 99.00, 62.00, 8.00, 72.00, 9.00, 97.00] +[71.00, 16.00, 73.00, 61.00, 92.00, 51.00, 1.00, 2.00] +[74.00, 84.00, 44.00, 70.00, 35.00, 66.00, 99.00, 15.00] +[71.00, 78.00, 14.00, 73.00, 71.00, 20.00, 27.00, 17.00] +[53.00, 76.00, 43.00, 9.00, 31.00, 77.00, 79.00, 48.00] +[64.00, 83.00, 83.00, 37.00, 51.00, 94.00, 87.00, 50.00] +[59.00, 57.00, 39.00, 37.00, 0.00, 87.00, 92.00, 23.00] +[25.00, 73.00, 39.00, 33.00, 15.00, 98.00, 0.00, 67.00 +2025-03-24T08:22:40.142Z INFO 291 --- [nio-8080-exec-1] org.example.WorkerController : Maximum element in matrix: 99.0 +2025-03-24T08:22:40.174Z INFO 291 --- [nio-8080-exec-1] org.example.WorkerController : Matrix after dividing by max element: +[0.18, 0.12, 1.00, 0.63, 0.08, 0.73, 0.09, 0.98] +[0.72, 0.16, 0.74, 0.62, 0.93, 0.52, 0.01, 0.02] +[0.75, 0.85, 0.44, 0.71, 0.35, 0.67, 1.00, 0.15] +[0.72, 0.79, 0.14, 0.74, 0.72, 0.20, 0.27, 0.17] +[0.54, 0.77, 0.43, 0.09, 0.31, 0.78, 0.80, 0.48] +[0.65, 0.84, 0.84, 0.37, 0.52, 0.95, 0.88, 0.51] +[0.60, 0.58, 0.39, 0.37, 0.00, 0.88, 0.93, 0.23] +[0.25, 0.74, 0.39, 0.33, 0.15, 0.99, 0.00, 0.68 + +``` +Вывод worker-service(2): +```powershell +:22:42.371Z INFO 336 --- [nio-8080-exec-1] org.example.WorkerController : Received request to divide matrix elements by max value +2025-03-24T08:22:42.384Z INFO 336 --- [nio-8080-exec-1] org.example.WorkerController : Received matrix: +[45.00, 54.00, 79.00, 60.00, 23.00, 8.00, 99.00, 93.00] +[27.00, 16.00, 95.00, 70.00, 25.00, 52.00, 29.00, 0.00] +[31.00, 94.00, 66.00, 32.00, 83.00, 66.00, 5.00, 26.00] +[38.00, 45.00, 2.00, 4.00, 86.00, 82.00, 66.00, 71.00] +[24.00, 6.00, 47.00, 94.00, 25.00, 87.00, 33.00, 99.00] +[90.00, 90.00, 95.00, 14.00, 58.00, 44.00, 56.00, 75.00] +[19.00, 29.00, 49.00, 18.00, 38.00, 91.00, 88.00, 41.00] +[89.00, 7.00, 75.00, 29.00, 23.00, 61.00, 37.00, 44.00 +2025-03-24T08:22:42.402Z INFO 336 --- [nio-8080-exec-1] org.example.WorkerController : Maximum element in matrix: 99.0 +2025-03-24T08:22:42.437Z INFO 336 --- [nio-8080-exec-1] org.example.WorkerController : Matrix after dividing by max element: +[0.45, 0.55, 0.80, 0.61, 0.23, 0.08, 1.00, 0.94] +[0.27, 0.16, 0.96, 0.71, 0.25, 0.53, 0.29, 0.00] +[0.31, 0.95, 0.67, 0.32, 0.84, 0.67, 0.05, 0.26] +[0.38, 0.45, 0.02, 0.04, 0.87, 0.83, 0.67, 0.72] +[0.24, 0.06, 0.47, 0.95, 0.25, 0.88, 0.33, 1.00] +[0.91, 0.91, 0.96, 0.14, 0.59, 0.44, 0.57, 0.76] +[0.19, 0.29, 0.49, 0.18, 0.38, 0.92, 0.89, 0.41] +[0.90, 0.07, 0.76, 0.29, 0.23, 0.62, 0.37, 0.44 +``` +### Вывод +В ходе выполнения лабораторной работы я разработал распределенное параллельное приложение на Spring Boot, использующее сервис-ориентированную архитектуру. Приложение успешно запускается в LXC-контейнерах, где мастер-сервис генерирует матрицу, делит её на части и отправляет на обработку работникам, которые выполняют вычисления и возвращают результат. В результате удалось реализовать эффективное распределение нагрузки, однако возможны дальнейшие оптимизации в части асинхронности запросов и объединения данных. \ No newline at end of file diff --git a/khaliullov_ramil_lab_3/WorkerService/.gitignore b/khaliullov_ramil_lab_3/WorkerService/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/khaliullov_ramil_lab_3/WorkerService/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/khaliullov_ramil_lab_3/WorkerService/src/main/java/org/example/App.java b/khaliullov_ramil_lab_3/WorkerService/src/main/java/org/example/App.java new file mode 100644 index 0000000..b630ea1 --- /dev/null +++ b/khaliullov_ramil_lab_3/WorkerService/src/main/java/org/example/App.java @@ -0,0 +1,11 @@ +package org.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} \ No newline at end of file diff --git a/khaliullov_ramil_lab_3/WorkerService/src/main/java/org/example/WorkerController.java b/khaliullov_ramil_lab_3/WorkerService/src/main/java/org/example/WorkerController.java new file mode 100644 index 0000000..260ea1b --- /dev/null +++ b/khaliullov_ramil_lab_3/WorkerService/src/main/java/org/example/WorkerController.java @@ -0,0 +1,97 @@ +package org.example; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.ResponseEntity; + +import java.util.Arrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@RestController +public class WorkerController { + + private static final Logger logger = LoggerFactory.getLogger(WorkerController.class); + + // Максимальное количество элементов, которые мы хотим выводить в логах + private static final int MAX_LOG_ELEMENTS = 64; // 8x8 = 64 элемента + + // Endpoint для разделения элементов матрицы на максимальный элемент + @PostMapping("/worker/divide") + public ResponseEntity divideMatrixByMax(@RequestBody double[][] matrix) { + // Логируем получение запроса от клиента + logger.info("Received request to divide matrix elements by max value"); + + // Логируем часть матрицы с ограничением по числу элементов + logMatrixPreview(matrix, "Received matrix"); + + // Находим максимальный элемент в матрице + double maxElement = findMaxElement(matrix); + logger.info("Maximum element in matrix: " + maxElement); + + // Разделяем все элементы матрицы на максимальное значение + double[][] dividedMatrix = divideMatrixByMax(matrix, maxElement); + + // Логируем обработанную матрицу + logMatrixPreview(dividedMatrix, "Matrix after dividing by max element"); + + // Возвращаем обработанную часть матрицы с HTTP-статусом 200 (OK) + return ResponseEntity.ok(dividedMatrix); + } + + // Метод для нахождения максимального элемента в матрице + private static double findMaxElement(double[][] matrix) { + double max = Double.MIN_VALUE; + for (double[] row : matrix) { + for (double element : row) { + if (element > max) { + max = element; + } + } + } + return max; + } + + // Метод для деления всех элементов матрицы на максимальный элемент + private static double[][] divideMatrixByMax(double[][] matrix, double maxElement) { + int rows = matrix.length; + int cols = matrix[0].length; + double[][] dividedMatrix = new double[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + dividedMatrix[i][j] = matrix[i][j] / maxElement; + } + } + + return dividedMatrix; + } + + // Метод для логирования части матрицы с ограничением по числу элементов + private void logMatrixPreview(double[][] matrix, String message) { + StringBuilder preview = new StringBuilder(message + ": \n"); + + int elementCount = 0; + outerLoop: + for (int i = 0; i < Math.min(8, matrix.length); i++) { // Логируем только 8 строк + preview.append("["); + + for (int j = 0; j < Math.min(8, matrix[i].length); j++) { // Логируем только 8 столбцов + preview.append(String.format("%.2f", matrix[i][j])).append(j < Math.min(8, matrix[i].length) - 1 ? ", " : ""); + elementCount++; + + if (elementCount >= MAX_LOG_ELEMENTS) { // 8x8 = 64 элемента + break outerLoop; + } + } + + preview.append("]"); + if (elementCount < MAX_LOG_ELEMENTS) { + preview.append("\n"); + } + } + + logger.info(preview.toString()); + } +} diff --git a/khaliullov_ramil_lab_3/WorkerService/src/main/resources/application.properties b/khaliullov_ramil_lab_3/WorkerService/src/main/resources/application.properties new file mode 100644 index 0000000..2928dc7 --- /dev/null +++ b/khaliullov_ramil_lab_3/WorkerService/src/main/resources/application.properties @@ -0,0 +1,2 @@ +server.port=8080 +spring.codec.max-in-memory-size=10MB