Shipilov_Nikita_lab_3

This commit is contained in:
2025-03-21 21:06:31 +04:00
parent c3d5ac3ba2
commit b9ab64e7dd
5 changed files with 269 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,34 @@
package com.example.matrixservice;
import org.springframework.web.bind.annotation.*;
import java.util.Random;
@RestController
@RequestMapping("/matrix")
public class MatrixProcessorController {
@PostMapping("/process")
public double[][] processMatrix(@RequestBody double[][] matrix) {
System.out.println("Get half matrix for processing");
double maxElement = findMax(matrix);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= maxElement;
}
}
System.out.println("Matrix successful proccessed");
return matrix;
}
private double findMax(double[][] matrix) {
double max = Double.MIN_VALUE;
for (double[] row : matrix) {
for (double value : row) {
if (value > max) {
max = value;
}
}
}
return max;
}
}

View File

@@ -0,0 +1,11 @@
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,87 @@
package com.example.matrixservice;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.Random;
@RestController
@RequestMapping("/matrix")
public class MatrixProcessorController {
private final String processHalfUrl = "http://192.168.28.128:8080/matrix/process";
@GetMapping("/process")
public String processMatrix(@RequestParam int size) {
RestTemplate restTemplate = new RestTemplate();
System.out.println("Program started...");
// 1. Генерируем матрицу
System.out.println("Generating matrix...");
Random rand = new Random();
double[][] matrix = new double[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = rand.nextDouble() * 100;
}
}
double maxElement = Arrays.stream(matrix)
.flatMapToDouble(Arrays::stream)
.max()
.orElse(1.0);
System.out.println("Matrix are generated! Size: " + size);
long startTime = System.currentTimeMillis();
System.out.println("Start processing...");
// 2. Делим матрицу на две части
double[][] firstHalf = new double[size / 2][size];
double[][] secondHalf = new double[size - size / 2][size];
System.arraycopy(matrix, 0, firstHalf, 0, size / 2);
System.arraycopy(matrix, size / 2, secondHalf, 0, size - size / 2);
// 3. Запускаем обработку локально и на первом сервисе параллельно
CompletableFuture<double[][]> processedFirstHalfFuture = CompletableFuture.supplyAsync(() -> processMatrixLocally(firstHalf));
CompletableFuture<double[][]> processedSecondHalfFuture = CompletableFuture.supplyAsync(() -> restTemplate.postForObject(processHalfUrl, secondHalf, double[][].class));
// 4. Ждем завершения обеих задач
CompletableFuture.allOf(processedFirstHalfFuture, processedSecondHalfFuture).join();
// 5. Получаем обработанные результаты
double[][] processedFirstHalf = processedFirstHalfFuture.join();
double[][] processedSecondHalf = processedSecondHalfFuture.join();
// 6. Объединяем результаты
double[][] resultMatrix = new double[size][size];
System.arraycopy(processedFirstHalf, 0, resultMatrix, 0, size / 2);
System.arraycopy(processedSecondHalf, 0, resultMatrix, size / 2, size - size / 2);
long endTime = System.currentTimeMillis();
System.out.println("Matrix processed success: " + (endTime - startTime) + "ms \n");
return "Matrix processed success: " + (endTime - startTime) + "ms \n";
}
private double[][] processMatrixLocally(double[][] matrix) {
double maxElement = findMax(matrix);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
matrix[i][j] /= maxElement;
}
}
return matrix;
}
private double findMax(double[][] matrix) {
double max = Double.MIN_VALUE;
for (double[] row : matrix) {
for (double value : row) {
if (value > max) {
max = value;
}
}
}
return max;
}
}

View File

@@ -0,0 +1,126 @@
# Лабораторная работа №3
Разработка распределенного приложения с использованием фреймворка Spring Boot.
### Необходимо:
- Разработать параллельный вариант алгоритма с применением сервис-ориентированного подхода и фреймворка Spring Boot, замерить время его работы.
**Вариант задания 1: Разделить элементы матрицы на наибольший элемент.**
### Как запустить лабораторную работу:
```sh
mvn spring-boot:run
```
Запускаем сервисы (делаем на обоих контейнерах).
```sh
curl http://192.168.28.129:8080/matrix/process?size=1000
```
Команда отправляет запрос на генерацию и обработку матрицы размером 1000x1000 (я запускал с виртуальной машины).
### Какие технологии использовали:
- **Spring Boot** — для создания REST API сервиса.
- **RestTemplate** — для отправки HTTP-запросов между сервисами.
- **CompletableFuture** — для асинхронного выполнения задач.
- **Docker + Proxmox** — для контейнеризации и тестирования распределенной системы.
### Как работает программа:
1. **Запуск сервиса**
- Программа запускается с помощью Spring Boot (`mvn spring-boot:run`), поднимая веб-сервер на порту 8080.
- Два контейнера (CT128 и CT129) работают независимо, обрабатывая части матрицы.
2. **Генерация матрицы**
- При получении GET-запроса на `http://192.168.28.129/matrix/process?size=N` создается квадратная матрица размером `N x N`.
- Значения элементов заполняются случайными числами от 0 до 100.
- Определяется максимальный элемент матрицы.
3. **Разделение матрицы**
- Матрица делится на две части: первая половина остается для локальной обработки, вторая отправляется на удаленный сервис (CT128).
- Данные передаются в виде JSON с помощью `RestTemplate.postForObject()`.
4. **Параллельная обработка**
- Для ускорения вычислений обе половины обрабатываются асинхронно с помощью `CompletableFuture.supplyAsync()`.
- Каждая часть матрицы нормализуется, деля каждый элемент на максимальный элемент матрицы.
5. **Сбор и объединение данных**
- После завершения вычислений результаты собираются и объединяются в финальную матрицу.
- Замеряется общее время выполнения и выводится в консоль.
6. **Ответ клиенту**
- Возвращается строка с временем выполнения обработки матрицы.
- Логи фиксируют процесс выполнения на обоих контейнерах.
### (Размер матрицы 1000х1000)
#### CT129 (На который поступал запрос от клиента)
```
Program started...
Generating matrix...
Matrix are generated! Size: 1000
Start processing...
Matrix processed success: 779ms
```
#### CT128 (Который обрабатывал половину матрицы)
```
Get half matrix for processing
Half matrix processed success: 2ms
```
### (Размер матрицы 2000х2000)
#### CT129 (На который поступал запрос от клиента)
```
Program started...
Generating matrix...
Matrix are generated! Size: 2000
Start processing...
Matrix processed success: 2966ms
```
#### CT128 (Который обрабатывал половину матрицы)
```
Get half matrix for processing
Half matrix processed success: 5ms
```
### Почему так происходит?
1. **Рост времени обработки при увеличении матрицы**:
- Время обработки на сервере CT129 растет быстрее, чем линейно, что может быть связано с увеличением накладных расходов на передачу данных и синхронизацию потоков.
- Время обработки на CT128 остается минимальным, так как он получает уже готовую половину матрицы и выполняет лишь нормализацию значений.
2. **Влияние сети**:
- Передача больших массивов данных через HTTP вызывает дополнительные задержки, что сказывается на общей производительности.
3. **Особенности Spring Boot**:
- Использование `RestTemplate` не является самым эффективным способом передачи данных. Возможен переход на WebFlux для обработки потоков данных без блокировки.
- `CompletableFuture` помогает параллелизировать задачи, но накладные расходы на переключение потоков влияют на общую скорость выполнения.
## Когда стоит использовать сервисный подход?
Использование сервисного подхода (Spring Boot) оправдано, если:
- Требуется **масштабируемость** (можно распределять обработку на несколько узлов).
- Важна **гибкость** (легко добавлять новые сервисы без изменения архитектуры).
- Обрабатываются **разнородные задачи** (например, обработка изображений, финансовые вычисления и пр.).
- Не критичны **задержки** при передаче данных по сети.
Если же задача требует высокой производительности и минимальных накладных расходов, лучше рассмотреть MPI или ForkJoin.
## Вывод
Spring Boot отлично подходит для разработки распределенных систем, но для вычислительно сложных задач, требующих минимальных задержек, стоит рассмотреть альтернативные парадигмы, такие как MPI или ForkJoin. При увеличении размера данных время обработки растет нелинейно из-за сетевых и потоковых накладных расходов. Оптимизация возможна за счет использования асинхронных технологий, таких как WebFlux, или перехода на другой подход к распределению вычислений.