Лабораторная работа №3.
Разработка распределенного приложения с использованием фреймворка Spring Boot
Вариант 5: Нужно определить максимальный элемент матрицы
Необходимо:
- Необходимо разработать параллельный вариант алгоритма с применением сервис-ориентированного подхода и фреймворка Spring Boot,
- Замерить время его работы.
Используемые технологии
- Spring Boot - фреймворк для упрощения создание и настройку Spring-приложений. А также используется для создания RESTful сервисов
- Docker
Запуск лабораторной работы
- docker build -t matrix-container2 . - создаем образ приложения во втором контейнере
- docker build -t matrix-container1 . - создаем образ приложения в первом контейнере
- docker run -d -p 8080:8080 matrix-container2 - запуск контейнера во втором контейнере
- docker run -d -p 8081:8081 matrix-container1 - запуск контейнера в первом контейнере
Что делает программа
- Генерирация матрицы
- Матрица разделяется на две половины. Первая половина матрицы остается в локальном сервисе. Вторая половина матрицы отправляется во второй сервис
- Паралелльая обработка матрицы и поиск каждым сервисом локального максимума в каждой половине.
- Определение глобального максимума
Обзор работы программы
Программа реализует распределенные вычисления в два контейнера.
- Первый контейнер генерирует матрицу размером 1000*1000
int[][] matrix = generateRandomMatrix(1000, 1000);
private static int[][] generateRandomMatrix(int rows, int columns) {
int[][] matrix = new int[rows][columns];
Random random = new Random();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
matrix[i][j] = random.nextInt(100_000);
}
}
return matrix;
}
- Затем в первом контейнер происходит разделение матрицы на две части
int half = matrix.length / 2;
int[][] firstHalf = new int[half][matrix[0].length];
int[][] secondHalf = new int[matrix.length - half][matrix[0].length];
for (int i = 0; i < matrix.length; i++) {
if (i < half) {
firstHalf[i] = matrix[i];
} else {
secondHalf[i - half] = matrix[i];
}
}
Первая половина будет обрабатываться локально, вторая половина будет обрабатывать на втором контейнере
- Распределенная обработка массива
Отправляем вторую половину матрицы во второй контейнер.
//создание объекта RestTemplate для выполнения HTTP-запроса
RestTemplate restTemplate = new RestTemplate();
//url адрес второго контейнера
String secondContainerUrl = "http://192.168.5.107:8080/matrix/max";
//отправка пост запроса второй половины матрицы на второй контейнер
ResponseEntity<Integer> response = restTemplate.postForEntity(secondContainerUrl, secondHalf, Integer.class);
Затем происходит поиск максимума в каждой половине массива.
В первом контейнере обработка половины массива происходит локально
private static int findMaxElement(int[][] matrix) {
int max = Integer.MIN_VALUE;
for (int[] row : matrix) {
for (int value : row) {
if (value > max) {
max = value;
}
}
}
return max;
}
Второй контейнер принимает половину матрицы, обрабатывает и отправляет обратно в первый контейнер
public int processMatrix(@RequestBody int[][] matrix) {
System.out.println("Server start find max");
int min = Integer.MAX_VALUE;
for (int[] row : matrix) {
for (int value : row) {
if (value < min) {
min = value;
}
}
}
return min;
}
Получение результата из второго контейнера и поиск глобального максимума из двух контейнеров
int localMax = findMaxElement(firstHalf);
//получение ответа из второго контейнера
int remoteMax = response.getBody();
int globalMax = Math.max(localMax, remoteMax);
Тесты
Вывод первого контейнера
root@secondcontainer:~/client# docker run matrix-container1
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.4.3)
2025-03-21T15:33:03.190Z INFO 1 --- [demo] [ main] com.example.client.Application : Starting Application v0.0.1-SNAPSHOT using Java 17.0.2 with PID 1 (/app.jar started by root in /)
2025-03-21T15:33:03.210Z INFO 1 --- [demo] [ main] com.example.client.Application : No active profile set, falling back to 1 default profile: "default"
2025-03-21T15:33:07.096Z INFO 1 --- [demo] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8081 (http)
2025-03-21T15:33:07.149Z INFO 1 --- [demo] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-03-21T15:33:07.155Z INFO 1 --- [demo] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.36]
2025-03-21T15:33:07.236Z INFO 1 --- [demo] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-03-21T15:33:07.243Z INFO 1 --- [demo] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3641 ms
2025-03-21T15:33:08.559Z INFO 1 --- [demo] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8081 (http) with context path '/'
2025-03-21T15:33:08.654Z INFO 1 --- [demo] [ main] com.example.client.Application : Started Application in 7.65 seconds (process running for 9.735)
Max: 99999
Time: 690 ms
Вывод второго контейнера
root@CT107:~/server# docker run matrix-container2
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.4.3)
2025-03-21T15:36:25.317Z INFO 1 --- [demo] [ main] com.example.server.Application : Starting Application v0.0.1-SNAPSHOT using Java 17.0.2 with PID 1 (/app.jar started by root in /)
2025-03-21T15:36:25.336Z INFO 1 --- [demo] [ main] com.example.server.Application : No active profile set, falling back to 1 default profile: "default"
2025-03-21T15:36:29.380Z INFO 1 --- [demo] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-03-21T15:36:29.537Z INFO 1 --- [demo] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-03-21T15:36:29.547Z INFO 1 --- [demo] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.36]
2025-03-21T15:36:30.043Z INFO 1 --- [demo] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-03-21T15:36:30.051Z INFO 1 --- [demo] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 4520 ms
2025-03-21T15:36:32.306Z INFO 1 --- [demo] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2025-03-21T15:36:32.417Z INFO 1 --- [demo] [ main] com.example.server.Application : Started Application in 8.98 seconds (process running for 11.068)
Server start find max
Вывод
В даной ЛР была был разработан параллельный вариант алгоритма при использования SpringBoot.
Если сравнивать алгоритм с однопоточным, то использование сервисной архитектуры приводит к более быстрому получению результата, если не брать в расчет случаи, когда дополнительные затраты на реализацию многопоточного приложения незначительны по сравнению с эффективностью паралельной обработки
Если сравнивать алгоритм с ThreadPoolExecutor(1440 ms), то использовать распределенности более выгодно.
Если сравнивать алгоритм с ForkJoinPool(464 ms) и MPI приложением(485 ms), то у нас получается более худшие результы, так как хоть приложение и распределенное, но обратывает части он линейно, в отличие от ForkJoinPool, который сильнее делит матрицу на подзадачи.