Files

Лабораторная работа №3

Задание

Разработать распределенное приложение с использованием фреймворка Spring Boot.

Необходимо: разработать параллельный вариант алгоритма с применением сервис-ориентированного подхода и фреймворка Spring Boot, замерить время его работы. Действия по варианту должны производиться в LXC контейнерах.

Вариант: упорядочить строки матрицы по возрастанию суммы их элементов (18).


Описание

Этот проект представляет собой распределенное приложение, разработанное с использованием фреймворка Spring Boot. Приложение состоит из двух сервисов, работающих в отдельных LXC-контейнерах:

  • MatrixMasterService (Ubuntu) — основной сервис, который генерирует матрицу, разделяет её на части, сортирует одну часть и отправляет другую часть на сортировку во второй сервис.

  • MatrixWorkerService (RockyLinux) — вспомогательный сервис, который получает часть матрицы, сортирует её и возвращает результат.

Приложение упорядочивает строки матрицы по возрастанию суммы их элементов.

Технологии

  • Spring Boot — фреймворк для создания микросервисов.

  • LXC — технология контейнеризации для запуска изолированных сред.

  • Maven — инструмент для сборки и управления зависимостями.

  • Java 17 — язык программирования (а также JDK под него).

  • REST API — для взаимодействия между сервисами.

  • SLF4J/Logback — для логирования.


Как работает программа

MatrixMasterService

1. Класс MatrixMasterServiceApplication

Этот класс является точкой входа в приложение. Он запускает Spring Boot приложение.

@SpringBootApplication
public class MatrixMasterServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(MatrixMasterServiceApplication.class, args);
    }
}

2. Класс MatrixController

Этот класс содержит REST-контроллер, который обрабатывает HTTP-запросы.

  • Метод generateAndProcessMatrix:
    • Логика работы:
      • Генерация случайной матрицы 8x8.
      • Разделение матрицы на две части.
      • Отправка первой половины в MatrixWorkerService.
      • Сортировка второй половины.
      • Получение отсортированной первой половины.
      • Сборка и вывод итоговой матрицы.
@GetMapping("/generate-and-process")
public int[][] generateAndProcessMatrix() {
    long startTime = System.currentTimeMillis();
    logger.info("Начало генерации и обработки матрицы");

    // Генерация матрицы 8x8
    int[][] matrix = generateRandomMatrix(8, 8);
    logger.info("Сгенерирована матрица:\n{}", formatMatrix(matrix));

    // Разделяем матрицу на две части
    int mid = matrix.length / 2;
    int[][] firstHalf = Arrays.copyOfRange(matrix, 0, mid);
    int[][] secondHalf = Arrays.copyOfRange(matrix, mid, matrix.length);

    // Отправляем первую половину в MatrixWorkerService
    logger.info("Отправка первой половины матрицы в MatrixWorkerService:\n{}", formatMatrix(firstHalf));
    int[][] sortedFirstHalf = sendToWorkerService(firstHalf);

    // Сортируем вторую половину в MatrixMasterService
    logger.info("Сортировка второй половины матрицы в MatrixMasterService:\n{}", formatMatrix(secondHalf));
    sortMatrixRows(secondHalf);
    logger.info("Отсортированная вторая половина матрицы:\n{}", formatMatrix(secondHalf));

    // Получаем отсортированную первую половину
    logger.info("Получена отсортированная первая половина матрицы:\n{}", formatMatrix(sortedFirstHalf));

    // Собираем отсортированные части
    int[][] result = new int[matrix.length][];
    System.arraycopy(sortedFirstHalf, 0, result, 0, sortedFirstHalf.length);
    System.arraycopy(secondHalf, 0, result, sortedFirstHalf.length, secondHalf.length);

    // Сортируем итоговую матрицу
    logger.info("Сортировка двух частей матрицы...\n");
    sortMatrixRows(result);
    logger.info("Итоговая отсортированная матрица:\n{}", formatMatrix(result));

    long endTime = System.currentTimeMillis();
    logger.info("Обработка матрицы завершена. Время выполнения: {} мс", endTime - startTime);

    return result;
}
  • Метод sortMatrixRows
    • Описание: Сортирует строки матрицы по возрастанию суммы их элементов.
    • Логика работы: использует Arrays.sort с компаратором, который сравнивает строки по сумме их элементов.
private void sortMatrixRows(int[][] matrix) {
Arrays.sort(matrix, Comparator.comparingInt(row -> Arrays.stream(row).sum()));
}
  • Метод sendToWorkerService
    • Описание: Отправляет часть матрицы в MatrixWorkerService.
    • Логика работы:
      • Использует RestTemplate для отправки POST-запроса.
      • Возвращает отсортированную часть матрицы.
private int[][] sendToWorkerService(int[][] matrix) {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://192.168.17.118:8081/sort";
    return restTemplate.postForObject(url, matrix, int[][].class);
}

MatrixWorkerService

1. Класс MatrixWorkerServiceApplication

Так же, как и класс MatrixMasterServiceApplication, этот класс является точкой входа в приложение. Он запускает Spring Boot приложение на втором контейнере.

@SpringBootApplication
public class MatrixWorkerServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(MatrixWorkerServiceApplication.class, args);
    }
}

2. Класс MatrixController

Этот класс содержит REST-контроллер, который обрабатывает HTTP-запросы (опять-таки по аналогии с классом в первом контейнере).

  • Метод sortMatrix
    • Описание: Сортирует часть матрицы, полученную от MatrixMasterService.
    • Логика работы:
      • Получает часть матрицы.
      • Сортирует строки по возрастанию суммы их элементов.
      • Возвращает отсортированную часть обратно.
@PostMapping("/sort")
public int[][] sortMatrix(@RequestBody int[][] matrix) {
    long startTime = System.currentTimeMillis();
    logger.info("Получена часть матрицы для сортировки:\n{}", formatMatrix(matrix));

    // Сортировка части матрицы
    Arrays.sort(matrix, Comparator.comparingInt(row -> Arrays.stream(row).sum()));
    logger.info("Сортировка части матрицы завершена:\n{}", formatMatrix(matrix));

    // Отправка отсортированной части обратно в MatrixMasterService
    logger.info("Отправка отсортированной части матрицы в MatrixMasterService...\n");

    long endTime = System.currentTimeMillis();
    logger.info("Время выполнения сортировки: {} мс", endTime - startTime);

    return matrix;
}

Как запустить программу

1. Создание проекта

В каждом контейнере создается Maven-проект с использованием Spring Boot:

mvn archetype:generate -DgroupId=com.example -DartifactId=matrix-master-service -Dversion=1.0 -Dpackage=com.example.matrixmasterservice -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Для MatrixWorkerService нужно заменить matrix-master-service и matrixmasterservice на matrix-worker-service и matrixworkerservice.

2. Настройка pom.xml:

Добавляем зависимости для Spring Boot и других библиотек в pom.xml каждого проекта.

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Родительский POM Spring Boot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.5</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>matrix-master-service</artifactId>
    <version>1.0</version>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Starter Web (для REST-контроллеров) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Lombok (для упрощения кода) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- Spring Boot Starter Test (для тестирования) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Spring Boot Maven Plugin -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3. Настройка application.properties:

В директории /src/main/resources каждого проекта создаем файл application.properties, где указываем порты подключения и уровень логирования.

server.port=8080
logging.level.com.example.matrixmasterservice=INFO

Для второго контейнера порт будет 8081-ым.

4. Сборка проекта

В каждом контейнере нужно выполнить команду для сборки проекта:

mvn clean package

5. Запуск приложения

Запускаем каждый сервис в соответствующем контейнере:

mvn spring-boot:run

6. Отправка запроса

Отправляем GET-запрос на MatrixMasterService для генерации и обработки матрицы:

curl http://192.168.17.117:8080/generate-and-process

Результат работы программы

Отправка запроса с хостовой машины

root@vbox:~# curl http://192.168.17.117:8080/generate-and-process

Ответ:

[[36,13,16,24,42,8,68,64],[57,27,84,5,0,66,21,14],[87,62,72,32,30,20,15,17],[86,29,85,16,14,47,62,13],[65,41,85,6,77,13,47,25],[73,41,67,66,0,11,47,74],[28,81,56,87,25,49,33,46],[88,66,44,44,85,31,97,52]]

Сервис 1

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Начало генерации и обработки матрицы
2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Сгенерирована матрица:
[87, 62, 72, 32, 30, 20, 15, 17]
[88, 66, 44, 44, 85, 31, 97, 52]
[36, 13, 16, 24, 42, 8, 68, 64]
[86, 29, 85, 16, 14, 47, 62, 13]
[57, 27, 84, 5, 0, 66, 21, 14]
[28, 81, 56, 87, 25, 49, 33, 46]
[65, 41, 85, 6, 77, 13, 47, 25]
[73, 41, 67, 66, 0, 11, 47, 74]

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Отправка первой половины матрицы в MatrixWorkerService:
[87, 62, 72, 32, 30, 20, 15, 17]
[88, 66, 44, 44, 85, 31, 97, 52]
[36, 13, 16, 24, 42, 8, 68, 64]
[86, 29, 85, 16, 14, 47, 62, 13]

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Сортировка второй половины матрицы в MatrixMasterService:
[57, 27, 84, 5, 0, 66, 21, 14]
[28, 81, 56, 87, 25, 49, 33, 46]
[65, 41, 85, 6, 77, 13, 47, 25]
[73, 41, 67, 66, 0, 11, 47, 74]

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Отсортированная вторая половина матрицы:
[57, 27, 84, 5, 0, 66, 21, 14]
[65, 41, 85, 6, 77, 13, 47, 25]
[73, 41, 67, 66, 0, 11, 47, 74]
[28, 81, 56, 87, 25, 49, 33, 46]

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Получена отсортированная первая половина матрицы:
[36, 13, 16, 24, 42, 8, 68, 64]
[87, 62, 72, 32, 30, 20, 15, 17]
[86, 29, 85, 16, 14, 47, 62, 13]
[88, 66, 44, 44, 85, 31, 97, 52]

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Сортировка двух частей матрицы...

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Итоговая отсортированная матрица:
[36, 13, 16, 24, 42, 8, 68, 64]
[57, 27, 84, 5, 0, 66, 21, 14]
[87, 62, 72, 32, 30, 20, 15, 17]
[86, 29, 85, 16, 14, 47, 62, 13]
[65, 41, 85, 6, 77, 13, 47, 25]
[73, 41, 67, 66, 0, 11, 47, 74]
[28, 81, 56, 87, 25, 49, 33, 46]
[88, 66, 44, 44, 85, 31, 97, 52]

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Обработка матрицы завершена. Время выполнения: 10 мс

Сервис 2

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Получена часть матрицы для сортировки:
[87, 62, 72, 32, 30, 20, 15, 17]
[88, 66, 44, 44, 85, 31, 97, 52]
[36, 13, 16, 24, 42, 8, 68, 64]
[86, 29, 85, 16, 14, 47, 62, 13]

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Сортировка части матрицы завершена:
[36, 13, 16, 24, 42, 8, 68, 64]
[87, 62, 72, 32, 30, 20, 15, 17]
[86, 29, 85, 16, 14, 47, 62, 13]
[88, 66, 44, 44, 85, 31, 97, 52]

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Отправка отсортированной части матрицы в MatrixMasterService...

2025-03-22 08:02:23 INFO  c.e.m.MatrixController - Время выполнения сортировки: 1 мс

Выводы

  • Удалось успешно разделить приложение на два сервиса, которые взаимодействуют через REST API.

  • Алгоритм генерации и сортировки матриц работает корректно (что видно по тестам).

  • Логирование помогло легко отследить выполнение операций и найти ошибки (особенно на ранних этапах работы).

  • Если говорить об эффективности данного метода разработки приложений (Spring Boot), то всплывают все те же минусы (как и у MPI) в виде накладных расходов на создание узлов и коммуникацию между ними.

  • Распределенная межкластерная система это довольно эффективный способ решения задач, которые можно разделить на независимые части. Однако такие системы требуют тщательной настройки и тестирования.