Compare commits

..

16 Commits

Author SHA1 Message Date
60ef5724cd Merge pull request 'sergeev_evgenii_lab_2 is done!' (#128) from sergeev_evgenii_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/128
2024-01-18 16:04:59 +04:00
6827a64c4d Merge pull request 'kutygin_andrey_lab_3_ready' (#120) from kutygin_andrey_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/120
2024-01-18 16:03:23 +04:00
7de577eadc Merge pull request 'podkorytova_yulia_lab_4 is ready' (#134) from podkorytova_yulia_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/134
2024-01-17 17:07:53 +04:00
2690438508 Merge pull request 'romanova_adelina_lab_4_ready' (#129) from romanova_adelina_lab_4 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/129
2024-01-17 13:12:17 +04:00
e3677ed302 Merge pull request 'kutygin_andrey_lab_2_ready' (#121) from kutygin_andrey_lab_2 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/121
2024-01-17 11:50:07 +04:00
yulia
f5bc94c4ec podkorytova_yulia_lab_4 is ready 2024-01-17 04:12:23 +04:00
f4ec46b14d romanova_adelina_lab_4_ready 2024-01-16 17:30:43 +04:00
ac085099f4 Merge pull request 'kutygin_lab_1_ready' (#122) from kutygin_andrey_lab_1 into main
Reviewed-on: http://student.git.athene.tech/Alexey/DAS_2023_1/pulls/122
2024-01-16 17:29:29 +04:00
sergeevevgen
46f2a8da94 + 2024-01-16 17:12:52 +04:00
Евгений Сергеев
84bd5277a9 done! 2024-01-16 16:57:20 +04:00
Евгений Сергеев
bb78e6823b done! 2024-01-16 16:05:42 +04:00
sergeevevgen
ea990fd848 + 2024-01-16 14:52:17 +04:00
a284e473a9 kutygin_andrey_lab_3_ready 2024-01-16 12:10:49 +04:00
c7ccb94de9 kutygin_andrey_lab_2_ready 2024-01-16 12:09:03 +04:00
0a6ce933c7 kutygin_lab_1_ready 2024-01-16 11:35:19 +04:00
Евгений Сергеев
36c429cb4f init 2024-01-13 19:17:58 +04:00
113 changed files with 2745 additions and 17 deletions

3
kutygin_andrey_lab_1/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JpaBuddyIdeaProjectConfig">
<option name="renamerInitialized" value="true" />
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="18" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="ProjectType">
<option name="id" value="jpab" />
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/kutygin_andrey_lab_1.iml" filepath="$PROJECT_DIR$/kutygin_andrey_lab_1.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="2825a2d7-cbe6-410f-9785-96603cbb0f02" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectId" id="2aWZgD9jVzSrrT33mt6jGLtpCOt" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"project.structure.last.edited": "Modules",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.2"
}
}]]></component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\Users\kutyg\Downloads\pibd-22-internet-programming-Lab5 (1)\kutygin_andrey_lab_1" />
</key>
</component>
<component name="RunManager">
<configuration name="docker-compose.yml.rabbitmq: Compose Deployment" type="docker-deploy" factoryName="docker-compose.yml" temporary="true" server-name="Docker">
<deployment type="docker-compose.yml">
<settings>
<option name="services">
<list>
<option value="rabbitmq" />
</list>
</option>
<option name="sourceFilePath" value="docker-compose.yml" />
</settings>
</deployment>
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Docker.docker-compose.yml.rabbitmq: Compose Deployment" />
</list>
</recent_temporary>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="2825a2d7-cbe6-410f-9785-96603cbb0f02" name="Changes" comment="" />
<created>1704436409086</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1704436409086</updated>
</task>
<servers />
</component>
</project>

View File

@ -0,0 +1,28 @@
**Задание**
***
Развернуть не менее 3х сервисов через docker-compose.
**Выполнение**
***
Было выбрано три сервиса: RabbitMQ, MediaWiki, WordPress.
Описание сервисов:
- "rabbitmq": использует образ rabbitmq:3.12.8-management и открывает порты 5672 и 15672 для доступа к RabbitMQ и его управляющему интерфейсу.
- "mediawiki": использует образ mediawiki и открывает порт 8082 для доступа к серверу Mediawiki.
- "wordpress": использует образ wordpress и открывает порт 8083 для доступа к серверу Wordpress.
Описание томов:
- "rabbitmq-data": том для хранения данных RabbitMQ.
- "mediawiki-data": том для хранения данных Mediawiki.
- "wordpress-data": том для хранения данных Wordpress.
**Результаты:**
***
![mediawiki.png](screenshots/mediawiki.png)
![rabbitmq.png](screenshots/rabbitmq.png)
![wordpress.png](screenshots/wordpress.png)
**Видео:**
[![Видео по лабораторной](https://avatars.mds.yandex.net/i?id=39a897a792179979ea684bfffbe0ca3ce053731c-10928048-images-thumbs&n=13)](https://disk.yandex.ru/i/-9wvRKd04dLa8w)
Нужно кликнуть на jpg, чтобы открылось

View File

@ -0,0 +1,27 @@
services:
rabbitmq:
image: rabbitmq:3.12.8-management
ports:
- 5672:5672
- 15672:15672
volumes:
- rabbitmq-data:/var/lib/rabbitmq
mediawiki:
image: mediawiki
ports:
- 8082:80
volumes:
- mediawiki-data:/var/files/mediawiki
wordpress:
image: wordpress
ports:
- 8083:80
volumes:
- wordpress-data:/var/files/wordpress
volumes:
rabbitmq-data:
mediawiki-data:
wordpress-data:

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

3
kutygin_andrey_lab_2/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JpaBuddyIdeaProjectConfig">
<option name="renamerInitialized" value="true" />
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_18" default="true" project-jdk-name="18" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="ProjectType">
<option name="id" value="jpab" />
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/kutygin_andrey_lab_2.iml" filepath="$PROJECT_DIR$/kutygin_andrey_lab_2.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="fb3a6329-81a8-41fd-8008-b8bea3f6c964" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectId" id="2ad3GS5xSIilTTmSvnMeDhv4jNu" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"last_opened_file_path": "C:/Users/kutyg/Downloads/pibd-22-internet-programming-Lab5 (1)/kutygin_andrey_lab_2/worker_2"
}
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\kutyg\Downloads\pibd-22-internet-programming-Lab5 (1)\kutygin_andrey_lab_2\worker_2" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\Users\kutyg\Downloads\pibd-22-internet-programming-Lab5 (1)\kutygin_andrey_lab_2\worker_2\src" />
<recent name="C:\Users\kutyg\Downloads\pibd-22-internet-programming-Lab5 (1)\kutygin_andrey_lab_2\worker_1\src" />
</key>
</component>
<component name="RunManager" selected="Docker.worker_2/Dockerfile">
<configuration name="docker-compose.yml: Compose Deployment" type="docker-deploy" factoryName="docker-compose.yml" temporary="true" server-name="Docker">
<deployment type="docker-compose.yml">
<settings>
<option name="sourceFilePath" value="docker-compose.yml" />
</settings>
</deployment>
<method v="2" />
</configuration>
<configuration name="worker_1/Dockerfile" type="docker-deploy" factoryName="dockerfile" temporary="true" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="sourceFilePath" value="worker_1/Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
<configuration name="worker_2/Dockerfile" type="docker-deploy" factoryName="dockerfile" temporary="true" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="sourceFilePath" value="worker_2/Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Docker.worker_2/Dockerfile" />
<item itemvalue="Docker.worker_1/Dockerfile" />
<item itemvalue="Docker.docker-compose.yml: Compose Deployment" />
</list>
</recent_temporary>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="fb3a6329-81a8-41fd-8008-b8bea3f6c964" name="Changes" comment="" />
<created>1704634533824</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1704634533824</updated>
</task>
<servers />
</component>
</project>

View File

@ -0,0 +1,121 @@
**Задание**
***
Цель: изучение техники создания простого распределённого приложения.
**Задачи**
***
Согласно варианту (0 и 1) разработать два приложения такие, что результат первого является исходными данными для второго.
Изучить файлы сборки образов docker и разработать их для созданных приложений.
Собрать файл docker-compose.yml для запуска приложений. Разобраться с монтированием каталогов из хост-системы.
Правильно закоммитить результат без лишних файлов.
Оформить pull request по правилам и отправить его на проверку.
**Ход работы**
***
**Разворачивание сервисов:**
***
Были разработаны два приложения на java:
worker-1 - Формирует файл /var/result/data.txt из файла, где было найдено самое большое число /var/data.
worker-2 - Ищет набольшее число из файла /var/result/data.txt и сохраняет его вторую степень в /var/result/result.txt.
**Исходные файлы**
Исходные файлы содержат целые числа.
В /var/result/data.txt проверяются числа из каждого файла и выбирается самое большое
**Firsttxt.txt:**
1
2
3
4
5
**Secondtxt.txt**
1
2
3
**Thirdtxt.txt**
1
2
3
4
5
6
7
8
9
10
В /var/result/result.txt записывается число 100 - квадрат наибольшего числа - 10
**Dockerfile**
Для данной работы были созданы идентичные докер-файлы в обоих проектах.
````
FROM openjdk:17
RUN mkdir /var/data
RUN mkdir /var/result
WORKDIR /app
COPY src /app/src
RUN javac /app/src/Main.java
CMD ["java", "-cp", "/app/src", "Main"]
````
FROM - выбор базового образа
RUN - создание директории внутри контейнера
WORKDIR - установка рабочей директории для последующих команд
COPY - копирование содержимого директории внутрь контейнера
RUN - компиляция исходного кода Main.java внутри контейнера
CMD - определение команды, которая выполняется при запуске контейнера. В данном случае происходит запуск программы на java и указывается пусть до Main.java
**docker-compose.yml**
````
version: "3" #формат конфигурации Docker Compose версии 3
services: #определение сервисов
worker1:
build:
context: /worker-1 #путь к контексту сборки
dockerfile: Dockerfile #имя докерфайла
volumes:
- .\var\data:/var/data #том для папки файлов
- .\var\result:/var/result #том для папки результатов
worker2:
- depends_on: #зависимость: worker2 не будет запущен, пока worker1 не завершит свой запуск
worker1
build:
context: /worker-2 #путь к контексту сборки
- dockerfile: Dockerfile #имя докерфайла
- volumes:
- .\var\result:/var/data #том для папки файлов
- .\var\result:/var/result #том для папки результатов
````
Видео: https://disk.yandex.ru/d/jpeOKQ_PAO0c0Q

View File

@ -0,0 +1,18 @@
version: "3" #формат конфигурации Docker Compose версии 3
services: #определение сервисов
worker1:
build:
context: /worker_1 #путь к контексту сборки
dockerfile: Dockerfile #имя докерфайла
volumes:
- .\var\data:/var/data #том для папки файлов
- .\var\result:/var/result #том для папки результатов
worker2:
depends_on: #зависимость: worker2 не будет запущен, пока worker1 не завершит свой запуск
- worker1
build:
context: /worker_2 #путь к контексту сборки
dockerfile: Dockerfile #имя докерфайла
volumes:
- .\var\result:/var/data #том для папки файлов
- .\var\result:/var/result #том для папки результатов

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,5 @@
1
2
3
4
5

View File

@ -0,0 +1,3 @@
1
2
3

View File

@ -0,0 +1,10 @@
1
2
3
4
5
6
7
8
9
10

View File

@ -0,0 +1,10 @@
1
2
3
4
5
6
7
8
9
10

View File

@ -0,0 +1 @@
100.0

View File

@ -0,0 +1,11 @@
FROM openjdk:17
RUN mkdir /var/data
RUN mkdir /var/result
WORKDIR /app
COPY src /app/src
RUN javac /app/src/Main.java
CMD ["java", "-cp", "/app/src", "Main"]

View File

@ -0,0 +1,40 @@
import java.io.*;
import java.nio.file.*;
//Ищет в каталоге /var/data самый большой по объёму файл и перекладывает его в /var/result/data.txt.
public class Main {
private static Path findLargestFile(DirectoryStream<Path> stream) throws IOException {
long maxSize = 0;
Path largestFile = null;
for (Path file : stream) {
long size = Files.size(file);
if (size > maxSize) {
maxSize = size;
largestFile = file;
}
}
return largestFile;
}
public static void main(String[] args) {
Path dataDir = Path.of("/var/data");
Path resultDir = Path.of("/var/result");
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dataDir)) {
Path largestFile = findLargestFile(stream);
if (largestFile != null) {
Files.createFile(resultDir.resolve("data.txt"));
Files.copy(largestFile, resultDir.resolve("data.txt"), StandardCopyOption.REPLACE_EXISTING);
System.out.println(largestFile.getFileName() + " перемещён");
} else {
System.out.println("Файл не найден");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,11 @@
FROM openjdk:17
RUN mkdir /var/data
RUN mkdir /var/result
WORKDIR /app
COPY src /app/src
RUN javac /app/src/Main.java
CMD ["java", "-cp", "/app/src", "Main"]

View File

@ -0,0 +1,50 @@
import java.io.*;
import java.nio.file.*;
import java.util.*;
//Ищет набольшее число из файла /var/data/data.txt и сохраняет его вторую степень в /var/result/result.txt.
public class Main {
public static void main(String[] args) {
Path dataFile = Path.of("/var/result/data.txt");
Path resultFile = Path.of("/var/result/result.txt");
try {
if (!Files.exists(dataFile)) {
System.out.println("Файл data.txt не существует");
return;
}
List<Integer> numbers = readNumbersFromFile(dataFile);
int maxNumber = findMaxNumber(numbers);
try (BufferedWriter writer = Files.newBufferedWriter(resultFile)) {
writer.write(String.valueOf(Math.pow(maxNumber,2)));
}
System.out.println("квадрат наибольшего числа " + Math.pow(maxNumber,2));
} catch (IOException e) {
e.printStackTrace();
}
}
private static List<Integer> readNumbersFromFile(Path dataFile) throws IOException {
List<Integer> numbers = new ArrayList<>();
try (BufferedReader reader = Files.newBufferedReader(dataFile)) {
String line;
while ((line = reader.readLine()) != null) {
numbers.add(Integer.parseInt(line));
}
}
return numbers;
}
private static int findMaxNumber(List<Integer> numbers) {
int maxNumber = Integer.MIN_VALUE;
for (int number : numbers) {
if (number > maxNumber) {
maxNumber = number;
}
}
return maxNumber;
}
}

3
kutygin_andrey_lab_3/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JpaBuddyIdeaProjectConfig">
<option name="renamerInitialized" value="true" />
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="ProjectType">
<option name="id" value="jpab" />
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/lab3.iml" filepath="$PROJECT_DIR$/.idea/lab3.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,110 @@
## Задание
Цель: изучение шаблона проектирования gateway, построения синхронного обмена между микросервисами и архитектурного стиля RESTful API.
Задачи:
- Создать 2 микросервиса, реализующих CRUD на связанных сущностях.
- Реализовать механизм синхронного обмена сообщениями между микросервисами.
- Реализовать шлюз на основе прозрачного прокси-сервера nginx.
## Ход работы
### Разворачивание сервисов:
Были разработаны два приложения на Java с использованием средства автоматизации сборки проектов Gradle и с использованием библиотеки spring-boot:
- categoryService - работа с дисциплинами (crud)
- productService - работа с продуктами (crud). При создании продукта выбирается id категории, и через nginx и rest template происходит получение данных о категории с этим id
### Обмен сообщениями
При создании плана обучения выбирается id категории, и через nginx и rest template происходит получение данных о категории с этим id
### Dockerfile
Идентичные докерфайлы для приложений:
```
FROM openjdk:17
RUN mkdir -p /usr/src/app/
WORKDIR /usr/src/app/
COPY . /usr/src/app/
RUN ./gradlew clean build
EXPOSE 8089
ENTRYPOINT ["java","-jar","build/libs/lab3-0.0.1-SNAPSHOT.jar"]
```
### docker-compose.yml
Файл, соединяющий сервисы (содержащий настройку Docker):
```
version: "3" #формат конфигурации Docker Compose версии 3
services: #определение сервисов
category:
build:
context: /categoryService #путь к контексту сборки
dockerfile: Dockerfile #имя докерфайла
ports:
- "8089:8089" #проброс портов
networks:
- netwrk #сеть
product:
build:
context: /productService #путь к контексту сборки
dockerfile: Dockerfile #имя докерфайла
ports:
- "8090:8090" #проброс портов
networks:
- netwrk #сеть
nginx:
image: nginx:latest #образ для контейнера
ports:
- "8091:80" #проброс портов
networks:
- netwrk #сеть
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf #монтирует локальный файл конфигурации
depends_on: #зависимость от сервисов
- category
- product
networks:
netwrk:
driver: bridge #изолированная сеть
```
### nginx.conf
Настройка nginx:
```
http {
server {
listen 80;
listen [::]:80;
server_name localhost;
location /categoryService/ {
proxy_pass http://category:8089/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /categoryService;
}
location /productService/ {
proxy_pass http://product:8090/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /productService;
}
}
}
events {
worker_connections 1024;
}
```
## Результат
Видео: https://disk.yandex.ru/d/8Lcvb0H9LPNSKw

View File

@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@ -0,0 +1,7 @@
FROM openjdk:17
RUN mkdir -p /usr/src/app/
WORKDIR /usr/src/app/
COPY . /usr/src/app/
RUN ./gradlew clean build
EXPOSE 8089
ENTRYPOINT ["java","-jar","build/libs/lab3-0.0.1-SNAPSHOT.jar"]

View File

@ -0,0 +1,34 @@
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'categoryApp'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2:2.1.210'
implementation 'org.hibernate.validator:hibernate-validator'
implementation 'org.springdoc:springdoc-openapi-ui:1.6.5'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
}
tasks.named('test') {
useJUnitPlatform()
}

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Wed Jan 10 18:16:23 GMT+04:00 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,2 @@
rootProject.name = "lab3"

View File

@ -0,0 +1,12 @@
package categoryApp;
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,48 @@
package categoryApp.controller;
import categoryApp.model.CategoryDto;
import org.springframework.web.bind.annotation.*;
import categoryApp.service.*;
import javax.validation.Valid;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/")
public class CategoryController {
private final CategoryService categoryService;
public CategoryController(CategoryService categoryService) {
this.categoryService = categoryService;
}
@GetMapping("/{id}")
public CategoryDto getCategory(@PathVariable Long id) {
return new CategoryDto(categoryService.findCategory(id));
}
@GetMapping("/")
public List<CategoryDto> getCategorys() {
return categoryService.findAllCategorys().stream()
.map(CategoryDto::new)
.collect(Collectors.toList());
}
@PostMapping("/")
public CategoryDto createCategory(@RequestBody @Valid CategoryDto categoryDto) {
return new CategoryDto(categoryService.addCategory(categoryDto));
}
@PutMapping("/{id}")
public CategoryDto updateCategory(@RequestBody @Valid CategoryDto categoryDto) {
return new CategoryDto(categoryService.updateCategory(categoryDto));
}
@DeleteMapping("/{id}")
public CategoryDto deleteCategory(@PathVariable Long id) {
return new CategoryDto(categoryService.deleteCategory(id));
}
}

View File

@ -0,0 +1,53 @@
package categoryApp.model;
import javax.persistence.*;
@Entity
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
public Category() {
}
public Category(String name, String description) {
this.name = name;
this.description = description;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Category {" +
"id =" + id +
", name ='" + name + '\'' +
", desc ='" + description + '\'' +
'}';
}
}

View File

@ -0,0 +1,40 @@
package categoryApp.model;
public class CategoryDto {
private Long id;
private String name;
private String description;
public CategoryDto() {
}
public CategoryDto(Category category) {
this.id = category.getId();
this.name = String.format("%s", category.getName());
this.description = category.getDescription();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -0,0 +1,7 @@
package categoryApp.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import categoryApp.model.Category;
public interface CategoryRepository extends JpaRepository<Category, Long> {
}

View File

@ -0,0 +1,7 @@
package categoryApp.service;
public class CategoryNotFoundException extends RuntimeException{
public CategoryNotFoundException(Long id) {
super(String.format("Category with id [%s] is not found", id));
}
}

View File

@ -0,0 +1,49 @@
package categoryApp.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import categoryApp.model.*;
import categoryApp.repository.*;
import java.util.*;
@Service
public class CategoryService {
private final CategoryRepository categoryRepository;
public CategoryService(CategoryRepository categoryRepository) {
this.categoryRepository = categoryRepository;
}
@Transactional
public Category addCategory(CategoryDto categoryDto) {
final Category category = new Category(categoryDto.getName(), categoryDto.getDescription());
return categoryRepository.save(category);
}
@Transactional(readOnly = true)
public Category findCategory(Long id) {
final Optional<Category> category = categoryRepository.findById(id);
return category.orElseThrow(() -> new CategoryNotFoundException(id));
}
@Transactional(readOnly = true)
public List<Category> findAllCategorys() {
return categoryRepository.findAll();
}
@Transactional
public Category updateCategory(CategoryDto categoryDto) {
final Category currentCategory = findCategory(categoryDto.getId());
currentCategory.setName(categoryDto.getName());
currentCategory.setDescription(categoryDto.getDescription());
return categoryRepository.save(currentCategory);
}
@Transactional
public Category deleteCategory(Long id) {
final Category currentCategory = findCategory(id);
categoryRepository.delete(currentCategory);
return currentCategory;
}
}

View File

@ -0,0 +1,11 @@
spring.main.banner-mode=off
spring.datasource.url=jdbc:h2:file:./data
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false
server.port=8089

View File

@ -0,0 +1,35 @@
version: "3" #формат конфигурации Docker Compose версии 3
services: #определение сервисов
category:
build:
context: /categoryService #путь к контексту сборки
dockerfile: Dockerfile #имя докерфайла
ports:
- "8089:8089" #проброс портов
networks:
- netwrk #сеть
product:
build:
context: /productService #путь к контексту сборки
dockerfile: Dockerfile #имя докерфайла
ports:
- "8090:8090" #проброс портов
networks:
- netwrk #сеть
nginx:
image: nginx:latest #образ для контейнера
ports:
- "8091:80" #проброс портов
networks:
- netwrk #сеть
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf #монтирует локальный файл конфигурации
depends_on: #зависимость от сервисов
- category
- product
networks:
netwrk:
driver: bridge #изолированная сеть

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

View File

@ -0,0 +1,27 @@
http {
server {
listen 80;
listen [::]:80;
server_name localhost;
location /categoryService/ {
proxy_pass http://category:8089/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /categoryService;
}
location /productService/ {
proxy_pass http://product:8090/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /productService;
}
}
}
events {
worker_connections 1024;
}

View File

@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@ -0,0 +1,7 @@
FROM openjdk:17
RUN mkdir -p /usr/src/app/
WORKDIR /usr/src/app/
COPY . /usr/src/app/
RUN ./gradlew clean build
EXPOSE 8089
ENTRYPOINT ["java","-jar","build/libs/lab3-0.0.1-SNAPSHOT.jar"]

View File

@ -0,0 +1,34 @@
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'categoryApp'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2:2.1.210'
implementation 'org.hibernate.validator:hibernate-validator'
implementation 'org.springdoc:springdoc-openapi-ui:1.6.5'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
}
tasks.named('test') {
useJUnitPlatform()
}

View File

@ -0,0 +1,6 @@
#Wed Jan 10 18:23:35 GMT+04:00 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,2 @@
rootProject.name = "lab3"

View File

@ -0,0 +1,19 @@
package productApp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@ -0,0 +1,48 @@
package productApp.controller;
import productApp.model.*;
import org.springframework.web.bind.annotation.*;
import productApp.service.*;
import javax.validation.Valid;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping("/{id}")
public ProductFullInfoDto getProduct(@PathVariable Long id) {
return productService.findProduct(id);
}
@GetMapping("/")
public List<ProductDto> getProducts() {
return productService.findAllProducts().stream()
.map(ProductDto::new)
.collect(Collectors.toList());
}
@PostMapping("/")
public ProductFullInfoDto createProduct(@RequestBody @Valid ProductDto productDto) {
return productService.addProduct(productDto);
}
@PutMapping("/{id}")
public ProductFullInfoDto updateProduct(@RequestBody @Valid ProductDto productDto) {
return productService.updateProduct(productDto);
}
@DeleteMapping("/{id}")
public ProductDto deleteClient(@PathVariable Long id) {
return new ProductDto(productService.deleteProduct(id));
}
}

View File

@ -0,0 +1,34 @@
package productApp.model;
import lombok.Data;
@Data
public class CategoryInfoDto {
private Long id;
private String name;
private String description;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -0,0 +1,71 @@
package productApp.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.persistence.*;
import lombok.Data;
@Data
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
private Long categoryId;
public Product() {
}
public Product(String name, String description, Long categoryId) {
this.name = name;
this.description = description;
this.categoryId = categoryId;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Long getCategoryId() {
return categoryId;
}
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}
@Override
public String toString() {
return "Category {" +
"id =" + id +
", time =" + name +
", description='" + description + '\'' +
", categoryId=" + categoryId +
'}';
}
}

View File

@ -0,0 +1,52 @@
package productApp.model;
import lombok.Data;
@Data
public class ProductDto {
private Long id;
private String name;
private String description;
private Long categoryId;
public ProductDto() {
}
public ProductDto(Product product) {
this.id = product.getId();
this.name = product.getName();
this.description = String.format("%s", product.getDescription());
this.categoryId = product.getCategoryId();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Long getCategoryId() {
return categoryId;
}
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}
}

View File

@ -0,0 +1,62 @@
package productApp.model;
import lombok.Data;
@Data
public class ProductFullInfoDto {
private Long id;
private String name;
private String description;
private Long categoryId;
private CategoryInfoDto categoryInfo;
public ProductFullInfoDto() {
}
public ProductFullInfoDto(Product product, CategoryInfoDto categoryInfo) {
this.id = product.getId();
this.name = product.getName();
this.description = product.getDescription();
this.categoryId = product.getCategoryId();
this.categoryInfo = categoryInfo;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Long getCategoryId() {
return categoryId;
}
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}
/*public CategoryInfoDto getCategoryInfo() {
return categoryInfo;
}
public void setCategoryInfo(CategoryInfoDto categoryInfo) {
this.categoryInfo = categoryInfo;
}*/
}

View File

@ -0,0 +1,7 @@
package productApp.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import productApp.model.Product;
public interface ProductRepository extends JpaRepository<Product, Long> {
}

View File

@ -0,0 +1,7 @@
package productApp.service;
public class ProductNotFoundException extends RuntimeException{
public ProductNotFoundException(Long id) {
super(String.format("Learning Plan with id [%s] is not found", id));
}
}

View File

@ -0,0 +1,73 @@
package productApp.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import productApp.model.*;
import productApp.repository.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.*;
@Service
public class ProductService {
private final ProductRepository productRepository;
private final RestTemplate restTemplate;
private final String NginxUrl = "http://nginx/categoryService/";
@Autowired
public ProductService(ProductRepository productRepository, RestTemplate restTemplate) {
this.productRepository = productRepository;
this.restTemplate = restTemplate;
}
@Transactional
public ProductFullInfoDto addProduct(ProductDto productDto) {
final Product product = new Product(productDto.getName(), productDto.getDescription(), productDto.getCategoryId());
productRepository.save(product);
CategoryInfoDto categoryInfo = restTemplate.getForObject(NginxUrl + productDto.getCategoryId(), CategoryInfoDto.class);
return new ProductFullInfoDto(product, categoryInfo);
}
@Transactional(readOnly = true)
public ProductFullInfoDto findProduct(Long id) {
final Product product = productRepository.findById(id).orElse(null);
if (product == null) {
throw new ProductNotFoundException(id);
}
CategoryInfoDto categoryInfo = restTemplate.getForObject(NginxUrl + product.getCategoryId(), CategoryInfoDto.class);
return new ProductFullInfoDto(product, categoryInfo);
}
@Transactional(readOnly = true)
public Product findProductClear(Long id) {
final Optional<Product> product = productRepository.findById(id);
return product.orElseThrow(() -> new ProductNotFoundException(id));
}
@Transactional(readOnly = true)
public List<Product> findAllProducts() {
return productRepository.findAll();
}
@Transactional
public ProductFullInfoDto updateProduct(ProductDto productDto) {
final Product currentProduct = findProductClear(productDto.getId());
currentProduct.setName(productDto.getName());
currentProduct.setDescription(productDto.getDescription());
currentProduct.setCategoryId(productDto.getCategoryId());
productRepository.save(currentProduct);
CategoryInfoDto categoryInfo = restTemplate.getForObject(NginxUrl + productDto.getCategoryId(), CategoryInfoDto.class);
return new ProductFullInfoDto(currentProduct, categoryInfo);
}
@Transactional
public Product deleteProduct(Long id) {
final Product currentProduct = findProductClear(id);
productRepository.delete(currentProduct);
return currentProduct;
}
}

View File

@ -0,0 +1,11 @@
spring.main.banner-mode=off
spring.datasource.url=jdbc:h2:file:./data
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false
server.port=8090

View File

@ -1,17 +0,0 @@
### Задание:
***
Эссе по вопросам:
* Какие алгоритмы и методы используются для балансировки нагрузки?
* Какие открытые и закрытые технологии существуют для балансировки нагрузки?
* Как осуществляется балансировка нагрузки на базах данных?
* Реверс-прокси как один из элементов балансировки нагрузки.
### Ответ:
***
Балансировка нагрузки - важная вещь в современных системах, целью которой является равномерное распределение запросов и нагрузки между серверами, чтобы все работало эффективно. Есть разные алгоритмы для этого, например, Round Robin или Weighted Round Robin. Еще бывают алгоритмы, основанные на наименьшей нагрузке или на быстром ответе сервера.
Примеры открытых технологий: HAProxy и Nginx. HAProxy - это реверс-прокси и балансировщик нагрузки, а Nginx - веб-сервер и прокси-сервер с хорошей производительностью.
Еще про балансировку нагрузки на базах данных - это горизонтальное масштабирование и репликация. Горизонтальное масштабирование делает базу данных распределенной, а репликация создает копии данных на нескольких узлах, что улучшает отказоустойчивость и возможность обработки больших нагрузок.
Реверс-прокси - это важный элемент балансировки нагрузки. Он принимает запросы от клиентов и перенаправляет их на серверы.

View File

@ -0,0 +1,45 @@
# Лабораторная работа 4. Работа с брокером сообщений
### Задание на лабораторную работу
1. Установить брокер сообщений RabbitMQ.
2. Пройти уроки 1, 2 и 3 из RabbitMQ Tutorials на любом языке программирования.
3. Продемонстрировать работу брокера сообщений.
***
### Описание работы
Были разработаны 3 приложения на *java*:
1. **Publisher**. Программа, которая создаёт один *exchange* с типом *fanout* и раз в секунду генерирует сообщение.
![](images/publisher.jpg)
2. **Consumer1**. Программа, которая создаёт под себя отдельную не анонимную очередь (*queue1*), создаёт *binding* на
*exchange* и начинает принимать сообщения. Программа обрабатывает сообщения 3 секунды.
![](images/consumer1.jpg)
3. **Consumer2**. Аналогично *Consumer1*, только сообщения обрабатываются моментально и имя очереди (*queue2*)
отличается от *Consumer1*.
![](images/consumer2.jpg)
***
### Отчеты
***RabbitMQ Management UI***
![](images/result1.jpg)
![](images/result2.jpg)
***Exchange***
![](images/result3.jpg)
***Очередь Consumer1***
![](images/queue1.jpg)
***Очередь Consumer2***
![](images/queue2.jpg)
### Ссылка на видео:
https://drive.google.com/file/d/19OdXnNM29SjayVZJ1qdsrMFTHkAUGxZf/view?usp=sharing

View File

@ -0,0 +1,50 @@
package org.example;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class Consumer1 {
private static final String EXCHANGE_NAME = "messages";
private static final String QUEUE_NAME = "queue1";
public static void main(String[] argv) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// Создание не анонимной очереди с уникальным именем
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) + " Consumer 1 ожидает сообщений...");
// Обработка сообщений
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody());
System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) + " Consumer 1 обрабатывает " + message);
try {
Thread.sleep(3000); // Обработка 3 секунды
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) + " Consumer 1 получил " + message);
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});
while (true) {
// Поддержание работы приложения
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,44 @@
package org.example;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class Consumer2 {
private static final String EXCHANGE_NAME = "messages";
private static final String QUEUE_NAME = "queue2";
public static void main(String[] argv) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// Создание не анонимной очереди с уникальным именем
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) + " Consumer 2 ожидает сообщений...");
// Обработка сообщений
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody());
// Обработка моментально, без задержки
System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) + " Consumer 2 получил " + message);
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});
while (true) {
// Поддержание работы приложения
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,33 @@
package org.example;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class Publisher {
private static final String EXCHANGE_NAME = "messages";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
while (true) {
String message = "сообщение от пользователя c id = " + Math.round((Math.random() * 100));
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) + " Отправлено: " + message);
Thread.sleep(1000); // Пауза в 1 секунду
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -0,0 +1,57 @@
# Лабораторная работа №4 - Работа с брокером сообщений
## Задание
#### Цель:
Изучение проектирования приложений при помощи брокера сообщений.
#### Задачи:
* Установить брокер сообщений RabbitMQ.
* Пройти уроки 1, 2 и 3 из RabbitMQ Tutorials на любом языке программирования.
* Продемонстрировать работу брокера сообщений.
### Классы:
&nbsp;1. ```Publisher``` - класс, отвечающий за отправку сообщений
&nbsp;2. ```Consumer1``` - класс, отвечающий за принятие и обработку сообщений за задержкой 3 секунды
&nbsp;2. ```Consumer2``` - класс, отвечающий за принятие и обработку сообщений без задержек
#### Ход работы:
На компьютер был установлен брокер сообщений ```RabbitMQ```, после чего все три класса программы были одновременно запущены.
## Работа программы:
Класс ```Publisher``` успешно осуществляет отправку сообщений своим клиентам.
![](Publisher.png "")
Класс ```Consumer1``` осуществляет принятие и обработку сообщений с задержкой в 3 секунды, это можно заметить на видео.
![](Consumer1.png "")
Класс ```Consumer2``` мгновенно осуществляет принятие и обработку сообщений.
![](Consumer2.png "")
## Работа с RabbitMQ Management UI
![](overview.png "")
### Очередь ```Consumer1```
![](qConsumer1.png "")
### Очередь ```Consumer2```
![](qConsumer2.png "")
### Exchange
![](ex.png "")
# Youtube
https://youtu.be/dw7j0WgDmS8

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,42 @@
package org.example;
import com.rabbitmq.client.*;
public class Consumer1 {
private static final String QUEUE_NAME = "queue1";
private static final String EXCHANGE_NAME = "exchange";
public static void main(String[] args) throws Exception {
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody());
try {
System.out.println("Consumer1 received" + message);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" Consumer1 processed the following message: " + message);
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});
System.out.println("Consumer1 is waiting for messages");
while (true) {
// Поддержание работы приложения
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,37 @@
package org.example;
import com.rabbitmq.client.*;
public class Consumer2 {
private static final String EXCHANGE_NAME = "exchange";
private static final String QUEUE_NAME = "queue2";
public static void main(String[] args) throws Exception {
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody());
System.out.println("Consumer2 received" + message);
// моментальная обработка сообщений
System.out.println("Consumer2 processed the following message: " + message);
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});
System.out.println("Consumer2 is waiting for messages");
while (true) {
// Поддержание работы приложения
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,35 @@
package org.example;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.util.Random;
public class Publisher {
private static final String EXCHANGE_NAME = "exchange";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
while (true) {
String message = generateMessage(); // Генерация сообщения для журнала событий
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println("Sent: " + message);
Thread.sleep(1000); // Ожидание 1 секунду
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static String generateMessage() {
Random random = new Random();
return "message №" + random.nextInt(100) + 1;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,42 @@
## Задание
Развернуть два взаимосвязанных сервиса по варианту:
Вариант № 4 для первой программы:
* Сервис формирует файл /var/result/data.txt так, что каждая строка файла - количество символов в именах файлов из каталога /var/data.
Вариант № 2 для второй программы:
* Сервис ищет наименьшее число из файла /var/data/data.txt и сохраняет его третью степень в /var/result/result.txt.
## Выполнение
Были написаны два сервиса на языке python с использованием технологии flask.
Они выводят на страницу кнопки, при нажатии на которые происходит соответствующие действия по заданию
Для сервисов прописаны файлы Dockerfile, описывающие создание контейнеров:
* Для обоих контейнеров выбирается Python 11
* На оба контейнера пробрасываются порты, на которых работает приложение: 8081 для первого и 8082 для второго
* Внутри контейнеров создаются папки /work для файлов скриптов, папки /var/result, /var/data для обоих сервисов
* В оба контейнера устанавливается фреймворк Flask
* Выбирается рабочая директория /work и туда копируются файлы скриптов
* Командой запускаются сами скрипты
Общий yml-файл настроен следующим образом:
* блок services, где перечислены разворачиваемые сервисы.
* для каждого сервиса прописан build, где обозначается его папка
* для каждого сервиса прописано пробрасывание портов на хостовую машину
* для каждого сервиса прописано отображение внутриконтейнерных папок на хостовые
## Результат
Пример выполнения:
Исходные данные: четыре файла в папке /var/data с разным по длине названием
Ход работы: нажатие кнопок на странице первого сервиса, потом - второго
Созданные контейнеры:
![Контейнеры](images/containers.png)
Страница первого задания:
![Страница первого задания](images/exercise1.png)
Выходные данные при выполнении второго задания:
![Второе задание. Результат](images/exercise2.png)
## Ссылка на видео
https://youtu.be/CEAAr0xolxM

View File

@ -0,0 +1,19 @@
version: '3'
services:
service1:
build:
context: ./service1
ports:
- "8081:8081"
volumes:
- .\var\data:/var/data
- .\var\result:/var/result
service2:
build:
context: ./service2
ports:
- "8082:8082"
volumes:
- ./var/result:/var/result

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Some files were not shown because too many files have changed in this diff Show More