This commit is contained in:
user
2025-03-25 01:43:22 +04:00
parent c3d5ac3ba2
commit 80141c3b9e
11 changed files with 523 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
package ru.pyzhov.MatrixGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MatrixGeneratorApplication {
public static void main(String[] args) {
SpringApplication.run(MatrixGeneratorApplication.class, args);
}
}

View File

@@ -0,0 +1,130 @@
package ru.pyzhov.MatrixGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@RestController
public class MatrixGeneratorController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private MatrixService matrixService;
private final String processorServiceUrl = "http://192.168.19.12:8080/sortMatrix";
@GetMapping("/generateAndSort")
public ResponseEntity<int[][]> generateAndSortMatrix(@RequestParam(defaultValue = "5") int size) {
long startTime = System.nanoTime();
System.out.println("Generating a matrix of size " + size + "x" + size);
int[][] matrix = generateMatrix(size);
System.out.println("Generated matrix:");
matrixService.printMatrix(matrix);
int[][] part1 = splitMatrix(matrix, 0, matrix.length / 2);
int[][] part2 = splitMatrix(matrix, matrix.length / 2, matrix.length);
System.out.println("Sorting the first part locally...");
CompletableFuture<int[][]> future1 = CompletableFuture.supplyAsync(() -> matrixService.sortByFirstElement(part1));
System.out.println("Sending the second part to Matrix Processing Service for sorting...");
CompletableFuture<int[][]> future2 = sendToProcessorAsync(part2, processorServiceUrl);
int[][] sortedPart1;
int[][] sortedPart2;
try {
sortedPart1 = future1.get();
sortedPart2 = future2.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
System.out.println("Sorted first part:");
matrixService.printMatrix(sortedPart1);
System.out.println("Received sorted second part:");
matrixService.printMatrix(sortedPart2);
System.out.println("Merging the two sorted parts...");
int[][] sortedMatrix = mergeMatrices(sortedPart1, sortedPart2);
System.out.println("Final sorted matrix:");
matrixService.printMatrix(sortedMatrix);
long endTime = System.nanoTime();
double durationMs = (endTime - startTime) / 1_000_000.0;
System.out.println("Total execution time: " + durationMs + " ms");
return ResponseEntity.ok(sortedMatrix);
}
private int[][] generateMatrix(int size) {
Random random = new Random();
int[][] matrix = new int[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = random.nextInt(100) - 50;
}
}
return matrix;
}
private int[][] splitMatrix(int[][] matrix, int start, int end) {
int[][] part = new int[end - start][matrix[0].length];
for (int i = start; i < end; i++) {
part[i - start] = matrix[i];
}
return part;
}
private CompletableFuture<int[][]> sendToProcessorAsync(int[][] matrix, String url) {
return CompletableFuture.supplyAsync(() -> {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<int[][]> request = new HttpEntity<>(matrix, headers);
ResponseEntity<int[][]> response = restTemplate.exchange(
url,
HttpMethod.POST,
request,
int[][].class
);
return response.getBody();
});
}
private int[][] mergeMatrices(int[][] part1, int[][] part2) {
int[][] result = new int[part1.length + part2.length][part1[0].length];
int i = 0, j = 0, k = 0;
while (i < part1.length && j < part2.length) {
if (part1[i][0] >= part2[j][0]) {
result[k++] = part1[i++];
} else {
result[k++] = part2[j++];
}
}
while (i < part1.length) {
result[k++] = part1[i++];
}
while (j < part2.length) {
result[k++] = part2[j++];
}
return result;
}
}

View File

@@ -0,0 +1,27 @@
package ru.pyzhov.MatrixGenerator;
import org.springframework.stereotype.Service;
import java.util.Arrays;
@Service
public class MatrixService {
public int[][] sortByFirstElement(int[][] matrix) {
Arrays.sort(matrix, (a, b) -> Integer.compare(b[0], a[0]));
return matrix;
}
public void printMatrix(int[][] matrix) {
int rowsToPrint = Math.min(matrix.length, 10);
int colsToPrint = matrix.length > 0 ? Math.min(matrix[0].length, 10) : 0;
for (int i = 0; i < rowsToPrint; i++) {
for (int j = 0; j < colsToPrint; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
}
}

View File

@@ -0,0 +1,13 @@
package ru.pyzhov.MatrixGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@@ -0,0 +1,2 @@
spring.application.name=MatrixGenerator
server.port=8081

View File

@@ -0,0 +1,13 @@
package ru.pyzhov.MatrixProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MatrixProcessingApplication {
public static void main(String[] args) {
SpringApplication.run(MatrixProcessingApplication.class, args);
}
}

View File

@@ -0,0 +1,26 @@
package ru.pyzhov.MatrixProcessing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MatrixProcessingController {
@Autowired
private MatrixService matrixService;
@PostMapping("/sortMatrix")
public ResponseEntity<int[][]> sortMatrix(@RequestBody int[][] matrix) {
System.out.println("Received matrix for sorting:");
matrixService.printMatrix(matrix);
int[][] sortedMatrix = matrixService.sortByFirstElement(matrix);
System.out.println("Sorted matrix:");
matrixService.printMatrix(sortedMatrix);
return ResponseEntity.ok(sortedMatrix);
}
}

View File

@@ -0,0 +1,27 @@
package ru.pyzhov.MatrixProcessing;
import org.springframework.stereotype.Service;
import java.util.Arrays;
@Service
public class MatrixService {
public int[][] sortByFirstElement(int[][] matrix) {
Arrays.sort(matrix, (a, b) -> Integer.compare(b[0], a[0]));
return matrix;
}
public void printMatrix(int[][] matrix) {
int rowsToPrint = Math.min(matrix.length, 10);
int colsToPrint = matrix.length > 0 ? Math.min(matrix[0].length, 10) : 0;
for (int i = 0; i < rowsToPrint; i++) {
for (int j = 0; j < colsToPrint; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
}
}

View File

@@ -0,0 +1,13 @@
package ru.pyzhov.MatrixProcessing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@@ -0,0 +1 @@
spring.application.name=MatrixProcessing

258
pyzhov_egor_lab_3/Readme.md Normal file
View File

@@ -0,0 +1,258 @@
# Лабораторная работа 3
## Задание
Разработка распределенного приложения с использованием фреймворка Spring Boot.
Действия по варианту должны производиться в LXC контейнерах. Необходимо
разработать параллельный вариант алгоритма с применением сервис-
ориентированного подхода и фреймворка Spring Boot, замерить время его работы.
**Вариант:** "Упорядочить строки матрицы по убыванию первых элементов."
## Описание работы программы
Программа реализует распределённую сортировку матрицы между двумя Spring Boot сервисами:
### Matrix Generator Service (основной сервис)
- Генерирует случайную квадратную матрицу
- Разделяет матрицу на две части
- Локально сортирует первую часть
- Отправляет вторую часть на сортировку в Matrix Processing Service
- Объединяет результаты в окончательную отсортированную матрицу
### Matrix Processing Service (вспомогательный сервис)
- Принимает часть матрицы для сортировки
- Сортирует полученную часть
- Возвращает отсортированный результат
## Описание кода
```java
@GetMapping("/generateAndSort")
public ResponseEntity<int[][]> generateAndSortMatrix(@RequestParam(defaultValue = "5") int size) {
long startTime = System.nanoTime();
System.out.println("Generating a matrix of size " + size + "x" + size);
int[][] matrix = generateMatrix(size);
System.out.println("Generated matrix:");
matrixService.printMatrix(matrix);
int[][] part1 = splitMatrix(matrix, 0, matrix.length / 2);
int[][] part2 = splitMatrix(matrix, matrix.length / 2, matrix.length);
System.out.println("Sorting the first part locally...");
CompletableFuture<int[][]> future1 = CompletableFuture.supplyAsync(() -> matrixService.sortByFirstElement(part1));
System.out.println("Sending the second part to Matrix Processing Service for sorting...");
CompletableFuture<int[][]> future2 = sendToProcessorAsync(part2, processorServiceUrl);
int[][] sortedPart1;
int[][] sortedPart2;
try {
sortedPart1 = future1.get();
sortedPart2 = future2.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
System.out.println("Sorted first part:");
matrixService.printMatrix(sortedPart1);
System.out.println("Received sorted second part:");
matrixService.printMatrix(sortedPart2);
System.out.println("Merging the two sorted parts...");
int[][] sortedMatrix = mergeMatrices(sortedPart1, sortedPart2);
System.out.println("Final sorted matrix:");
matrixService.printMatrix(sortedMatrix);
long endTime = System.nanoTime();
double durationMs = (endTime - startTime) / 1_000_000.0;
System.out.println("Total execution time: " + durationMs + " ms");
return ResponseEntity.ok(sortedMatrix);
}
```
Основной endpoint, который генерирует матрицу заданного размера, разделяет ее на две части, сортирует их параллельно (одну локально, другую через внешний сервис) и возвращает объединенный результат.
```java
CompletableFuture<int[][]> future1 = CompletableFuture.supplyAsync(() -> matrixService.sortByFirstElement(part1));
CompletableFuture<int[][]> future2 = sendToProcessorAsync(part2, processorServiceUrl);
```
Использование CompletableFuture для параллельной обработки частей матрицы - ключевая особенность архитектуры, позволяющая ускорить обработку.
```java
private CompletableFuture<int[][]> sendToProcessorAsync(int[][] matrix, String url) {
return CompletableFuture.supplyAsync(() -> {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<int[][]> request = new HttpEntity<>(matrix, headers);
ResponseEntity<int[][]> response = restTemplate.exchange(
url,
HttpMethod.POST,
request,
int[][].class
);
return response.getBody();
});
}
```
Асинхронная отправка части матрицы на внешний сервис для обработки с использованием RestTemplate.
```java
@PostMapping("/sortMatrix")
public ResponseEntity<int[][]> sortMatrix(@RequestBody int[][] matrix) {
System.out.println("Received matrix for sorting:");
matrixService.printMatrix(matrix);
int[][] sortedMatrix = matrixService.sortByFirstElement(matrix);
System.out.println("Sorted matrix:");
matrixService.printMatrix(sortedMatrix);
return ResponseEntity.ok(sortedMatrix);
}
```
Асинхронная отправка части матрицы на внешний сервис для обработки с использованием RestTemplate.
```java
public int[][] sortByFirstElement(int[][] matrix) {
Arrays.sort(matrix, (a, b) -> Integer.compare(b[0], a[0]));
return matrix;
}
```
Основная логика сортировки строк матрицы по первому элементу в порядке убывания, используемая в обоих сервисах.
```java
private int[][] mergeMatrices(int[][] part1, int[][] part2) {
int[][] result = new int[part1.length + part2.length][part1[0].length];
int i = 0, j = 0, k = 0;
while (i < part1.length && j < part2.length) {
if (part1[i][0] >= part2[j][0]) {
result[k++] = part1[i++];
} else {
result[k++] = part2[j++];
}
}
while (i < part1.length) {
result[k++] = part1[i++];
}
while (j < part2.length) {
result[k++] = part2[j++];
}
return result;
}
```
Метод для слияния двух отсортированных частей матриц
## Запуск приложения
На каждом контейнере надо запустить jar файл
В контейнере MatrixGenerator:
```bash
java -jar MatrixGenerator-0.0.1-SNAPSHOT.jar
```
В контейнере MatrixProcessing:
```bash
java -jar MatrixProcessing-0.0.1-SNAPSHOT.jar
```
В `MatrixGeneratorController.java` надо указать адрес второго контейнера, на котором находиться MatrixProcessing
```java
private final String processorServiceUrl = "http://192.168.19.12:8080/sortMatrix";
```
## Основные технологии
- **Spring Boot** - фреймворк для создания приложения
- **RestTemplate** - синхронные HTTP-вызовы к внешнему сервису
- **CompletableFuture** асинхронное выполнение задач
## Результат работы программы
```bash
curl -X GET http://192.168.19.11:8081/generateAndSort?size=6
```
На первом контейнере:
```bash
Generating a matrix of size 6x6
Generated matrix:
7 -21 -22 -24 -38 18
-5 -39 -2 33 18 -25
-6 2 48 25 -5 33
19 18 -21 -17 -9 11
-25 -7 3 42 41 1
-39 -46 49 20 -27 -16
Sorting the first part locally...
Sorted first part:
7 -21 -22 -24 -38 18
-5 -39 -2 33 18 -25
-6 2 48 25 -5 33
Sending the second part to Matrix Processing Service for sorting...
Received sorted second part:
19 18 -21 -17 -9 11
-25 -7 3 42 41 1
-39 -46 49 20 -27 -16
Merging the two sorted parts...
Final sorted matrix:
19 18 -21 -17 -9 11
7 -21 -22 -24 -38 18
-5 -39 -2 33 18 -25
-6 2 48 25 -5 33
-25 -7 3 42 41 1
-39 -46 49 20 -27 -16
Total execution time: 47.574409 ms
```
На втором:
```bash
Received matrix for sorting:
19 18 -21 -17 -9 11
-25 -7 3 42 41 1
-39 -46 49 20 -27 -16
Sorted matrix:
19 18 -21 -17 -9 11
-25 -7 3 42 41 1
-39 -46 49 20 -27 -16
```
Были проведены замеры работы программы на разных объёмах данных:
size 3000: 485.010414 ms
size 3000: 522.710461 ms
size 5000: 1369.910234 ms
size 5000: 1342.053429 ms
size 10000: 5868.467635 ms
size 10000: 4471.374621 ms
Для сравнения я протестировал программу без параллельной сортировки, отправляя всю матрицу на второй контейнер:
size 3000: 688.015384 ms
size 3000: 998.452761 ms
size 5000: 2319.916129 ms
size 5000: 1923.826935 ms
size 10000: 8066.866806 ms
size 10000: 8199.054357 ms
Преимущество параллельной работы очевидно.
## Вывод
Сравнивая результаты с другими реализациями алгоритма в прошлых лабораторных работах, а именно MPI, ForkJoin, ThreadPool, можно сделать вывод, что ForkJoin и ThreadPool намного эффективнее в таком виде задачи. Проблема в сетевых и потоковых накладных расходах.
Возможно изменение подхода RestTemplate на WebFlux, дал бы прироста к производительности и эффективности программы.
Распределенное приложение на Spring Boot хороший выбор, если мы используем микросервисную архитектуру, и нам нужно масштабируемое приложение. Если нам нужно решить простую задачу локально, то лучше присмотреться к ForkJoin, ThreadPool, если нам нужно решить задачу с высокой вычислительной сложность в распределительной среде, то лучшим выбором будет MPI.