forked from sevastyan_b/SSPR_25
ready
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
spring.application.name=MatrixGenerator
|
||||
server.port=8081
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
spring.application.name=MatrixProcessing
|
||||
258
pyzhov_egor_lab_3/Readme.md
Normal file
258
pyzhov_egor_lab_3/Readme.md
Normal 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.
|
||||
|
||||
Reference in New Issue
Block a user