Отчет по лабораторной работе
Описание программы
Этот проект состоит из двух микросервисов, которые выполняют асинхронные вычисления для матриц. Каждый сервис обрабатывает строку матрицы, выполняя умножение всех элементов строки или их сумму в зависимости от сервиса. Для работы используются технологии Spring Boot и асинхронные вычисления с использованием CompletableFuture.
Как запустить программу
-
Клонируйте репозиторий:
git clone https://github.com/ваш-репозиторий.git -
Перейдите в каталог каждого проекта:
cd client1 -
Соберите проекты: Для обоих проектов выполните команду:
mvn package -
Построение Docker-образов:
- Для проекта
client1:docker build -t client1 -f .dockerfile . - Для проекта
client2:docker build -t client2 -f .dockerfile .
- Для проекта
-
Переместите образы в контейнеры: Docker образы должны быть запущены на разных узлах:
- Образ
client1должен быть запущен на узле с IP192.168.9.*, кроме192.168.9.11и192.168.9.12. - Образ
client2должен быть запущен на узлах с IP192.168.9.11и192.168.9.12.
- Образ
-
Узлы должны быть настроены так, чтобы они могли обмениваться данными друг с другом.
Используемые технологии
- Язык программирования: Java 21
- Фреймворк: Spring Boot
- Асинхронное программирование: CompletableFuture
- Для взаимодействия между сервисами используется Feign Client
- Для контейнеризации используется Docker
Client 1 описание кода
@FeignClient(name ="Client2", url = "http://192.168.9.11:8080")
public interface Client2F {
// Feign клиент чтобы не строить запросы ручками
@PostMapping("/async")
long getAsync(@RequestBody int[][] matrix);
}
--------
@Service
@Slf4j
@RequiredArgsConstructor
public class ServiceTask { // Класс распределения
private final Client2F feignClient1; // Объекты заинжектятся через конструктор
private final Client3F feignClient2;
@Async // Объект таски
public CompletableFuture<Long> calculateProductAsync(int[] row, int i) {
try {
// Обращение к сервисам
if (i % 2 == 0) {
return CompletableFuture.completedFuture(feignClient1.getAsync(new int[][]{row}));
} else {
return CompletableFuture.completedFuture(feignClient2.getAsync(new int[][]{row}));
}
}
catch (Exception ex){
log.info(ex.getMessage());
System.exit(0);
}
return CompletableFuture.completedFuture(0L);
}
@Async // Результирующий объект
public CompletableFuture<Long> calculateSumOfRowWithAsync(int[][] matrix) throws ExecutionException, InterruptedException {
long sum = 0;
int f = 0;
// Распределение по строкам
List<CompletableFuture<Long>> futures = new ArrayList<>();
for (int[] row : matrix) {
f = (f + 1) % 2;
futures.add(calculateProductAsync(row, f));
}
for (CompletableFuture<Long> future : futures) {
try {
sum += future.get();
} catch (ExecutionException | InterruptedException e) {
System.out.println(e.getMessage());
}
}
return CompletableFuture.completedFuture(sum);
}
}
Client 2 описание кожа
private final ServiceTask serviceTask;
@PostMapping("/async") // Контроллер принятия данных
public long getAsync(@RequestBody int[][] map) throws ExecutionException, InterruptedException, JsonProcessingException {
System.out.println("Матрица размером " + map.length);
var res = serviceTask.calculateSumOfRowWithAsync(map);
log.info(Thread.currentThread().getName() + " Результат: " + res.get());
return res.get();
}
-----
public class ServiceTask {
@Async
public CompletableFuture<Integer> calculateProductAsync(int[] row) { // Вычисление
int product = 1;
for (int value : row) {
product *= value;
}
return CompletableFuture.completedFuture(product);
}
@Async // Распределение вычислений
public CompletableFuture <Integer> calculateSumOfRowWithAsync(int[][] matrix) throws ExecutionException, InterruptedException {
int sum = 0;
List<CompletableFuture<Integer>> futures = new ArrayList<>();
for (int[] row : matrix) {
futures.add(calculateProductAsync(row));
}
for (CompletableFuture<Integer> future : futures) {
try {
sum += future.get();
} catch (ExecutionException | InterruptedException e) {
System.out.println(e.getMessage());
}
}
return CompletableFuture.completedFuture(sum);
}
}
Описание работы программы
В проекте два сервиса с асинхронной обработкой данных:
-
ServiceTask: Этот класс предоставляет два метода для асинхронных вычислений:
calculateProductAsync: вычисляет произведение всех элементов в строке матрицы.calculateSumOfRowWithAsync: асинхронно вычисляет сумму произведений всех строк матрицы.
-
В проекте
client2используется дополнительный классClient2F, который через Feign Client взаимодействует с другим сервисом и вызывает вычисления в нем, в зависимости от номера строки.
Структура кода:
-
ServiceTask:
- Основной класс, отвечающий за вычисления с матрицами.
- Использует
@Asyncдля выполнения вычислений асинхронно. - Метод
calculateProductAsyncвыполняет умножение для каждой строки матрицы. - Метод
calculateSumOfRowWithAsyncсобирает все асинхронные вычисления и возвращает суммарный результат.
-
Client2F:
- Feign Client, который асинхронно взаимодействует с другим сервисом для выполнения вычислений.
Примеры тестов
Вывод
- Client1
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.2)
2025-03-02T13:48:37.474Z INFO 1 --- [ main] org.urlshort.App : Starting App v1.0-SNAPSHOT using Java 21 with PID 1 (/app.jar started by root in /)
2025-03-02T13:48:37.499Z INFO 1 --- [ main] org.urlshort.App : No active profile set, falling back to 1 default profile: "default"
2025-03-02T13:48:38.887Z INFO 1 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=ceae2d3b-8dc4-30da-9da6-f9b9bc608a4c
2025-03-02T13:48:39.282Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-03-02T13:48:39.308Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-03-02T13:48:39.308Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.26]
2025-03-02T13:48:39.365Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-03-02T13:48:39.367Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1631 ms
2025-03-02T13:48:40.048Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2025-03-02T13:48:40.087Z INFO 1 --- [ main] org.urlshort.App : Started App in 3.622 seconds (process running for 7.345)
Введите размер матрицы (от 2 до 1000): 10
2025-03-02T13:48:54.348Z INFO 1 --- [ main] org.urlshort.TaskRun : Матрица создана ----------------------------------------
2025-03-02T13:48:54.847Z INFO 1 --- [ main] org.urlshort.TaskRun : Вычисления выполнены за: 497800181
- Client2.1 (для client2.2 вывод аналогичен)
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.2)
2025-03-02T13:44:44.725Z INFO 1 --- [ main] org.urlshort.App : Starting App v1.0-SNAPSHOT using Java 21 with PID 1 (/app.jar started by root in /)
2025-03-02T13:44:44.732Z INFO 1 --- [ main] org.urlshort.App : No active profile set, falling back to 1 default profile: "default"
2025-03-02T13:44:46.111Z INFO 1 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=cbd0db31-b26c-3fb9-8542-dcebb5489091
2025-03-02T13:44:46.626Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-03-02T13:44:46.669Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-03-02T13:44:46.670Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.26]
2025-03-02T13:44:46.736Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-03-02T13:44:46.737Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1840 ms
2025-03-02T13:44:47.288Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2025-03-02T13:44:47.320Z INFO 1 --- [ main] org.urlshort.App : Started App in 3.431 seconds (process running for 4.727)
2025-03-02T13:48:54.627Z INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-03-02T13:48:54.627Z INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-03-02T13:48:54.629Z INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
Матрица размером 1
2025-03-02T13:48:54.694Z INFO 1 --- [nio-8080-exec-1] org.urlshort.Cont : http-nio-8080-exec-1 Результат: 1214715904
Матрица размером 1
2025-03-02T13:48:54.733Z INFO 1 --- [nio-8080-exec-2] org.urlshort.Cont : http-nio-8080-exec-2 Результат: -134498304
Матрица размером 1
2025-03-02T13:48:54.755Z INFO 1 --- [nio-8080-exec-4] org.urlshort.Cont : http-nio-8080-exec-4 Результат: 0
Матрица размером 1
2025-03-02T13:48:54.793Z INFO 1 --- [nio-8080-exec-5] org.urlshort.Cont : http-nio-8080-exec-5 Результат: 0
Матрица размером 1
2025-03-02T13:48:54.838Z INFO 1 --- [nio-8080-exec-6] org.urlshort.Cont : http-nio-8080-exec-6 Результат: 0
Выводы по лабораторной работе
В данной лабораторной работе были реализованы два асинхронных сервиса . Основным инструментом для асинхронных вычислений стал CompletableFuture. Также был использован Feign Client для взаимодействия между сервисами.
Довольно интересно поднимать rest api, но есть ощущение что выполнять математические праспределения с помощью spring не слишком нужно
Больше подходит для распределение бизнес логики