Compare commits

..

No commits in common. "main" and "vasina_ekaterina_lab_4" have entirely different histories.

454 changed files with 0 additions and 11861 deletions

57
.gitignore vendored
View File

@ -1,57 +0,0 @@
################################################################################
# Данный GITIGNORE-файл был автоматически создан Microsoft(R) Visual Studio.
################################################################################
/.vs/DAS_2024_1
/.vs
/aleikin_artem_lab_3/.vs
/aleikin_artem_lab_3/ProjectEntityProject/bin/Debug/net8.0
/aleikin_artem_lab_3/ProjectEntityProject/obj
/aleikin_artem_lab_3/TaskProject/bin/Debug/net8.0
/aleikin_artem_lab_3/TaskProject/obj/Container
/aleikin_artem_lab_3/TaskProject/obj
/aleikin_artem_lab_4/RVIPLab4/.vs
/aleikin_artem_lab_4/RVIPLab4/Consumer1/bin/Debug/net8.0
/aleikin_artem_lab_4/RVIPLab4/Consumer1/obj
/aleikin_artem_lab_4/RVIPLab4/Consumer2/bin/Debug/net8.0
/aleikin_artem_lab_4/RVIPLab4/Consumer2/obj
/aleikin_artem_lab_4/RVIPLab4/FirstTutorial/Receive/bin/Debug/net8.0
/aleikin_artem_lab_4/RVIPLab4/FirstTutorial/Receive/obj
/aleikin_artem_lab_4/RVIPLab4/FirstTutorial/Send/bin/Debug/net8.0
/aleikin_artem_lab_4/RVIPLab4/FirstTutorial/Send/obj
/aleikin_artem_lab_4/RVIPLab4/Publisher/bin/Debug/net8.0
/aleikin_artem_lab_4/RVIPLab4/Publisher/obj
/aleikin_artem_lab_4/RVIPLab4/SecondTutorial/NewTask/bin/Debug/net8.0
/aleikin_artem_lab_4/RVIPLab4/SecondTutorial/NewTask/obj
/aleikin_artem_lab_4/RVIPLab4/SecondTutorial/Worker/bin/Debug/net8.0
/aleikin_artem_lab_4/RVIPLab4/SecondTutorial/Worker/obj
/aleikin_artem_lab_4/RVIPLab4/ThirdTutorial/EmitLog/bin/Debug/net8.0
/aleikin_artem_lab_4/RVIPLab4/ThirdTutorial/EmitLog/obj
/aleikin_artem_lab_4/RVIPLab4/ThirdTutorial/ReceiveLogs/bin/Debug/net8.0
/aleikin_artem_lab_4/RVIPLab4/ThirdTutorial/ReceiveLogs/obj
/dozorova_alena_lab_2
/dozorova_alena_lab_3
/dozorova_alena_lab_4
/dozorova_alena_lab_5/ConsoleApp1/obj
/dozorova_alena_lab_6/ConsoleApp1/obj
/aleikin_artem_lab_4/RVIPLab4/RVIPLab4.sln
/aleikin_artem_lab_4/.vs
/aleikin_artem_lab_4/Consumer1/bin/Debug/net8.0
/aleikin_artem_lab_4/Consumer1/obj
/aleikin_artem_lab_4/Consumer2/bin/Debug/net8.0
/aleikin_artem_lab_4/Consumer2/obj
/aleikin_artem_lab_4/FirstTutorial/Receive/bin/Debug/net8.0
/aleikin_artem_lab_4/FirstTutorial/Receive/obj
/aleikin_artem_lab_4/FirstTutorial/Send/bin/Debug/net8.0
/aleikin_artem_lab_4/FirstTutorial/Send/obj
/aleikin_artem_lab_4/Publisher/bin/Debug/net8.0
/aleikin_artem_lab_4/Publisher/obj
/aleikin_artem_lab_4/SecondTutorial/NewTask/bin/Debug/net8.0
/aleikin_artem_lab_4/SecondTutorial/NewTask/obj
/aleikin_artem_lab_4/SecondTutorial/Worker/bin/Debug/net8.0
/aleikin_artem_lab_4/SecondTutorial/Worker/obj
/aleikin_artem_lab_4/ThirdTutorial/EmitLog/bin/Debug/net8.0
/aleikin_artem_lab_4/ThirdTutorial/EmitLog/obj
/aleikin_artem_lab_4/ThirdTutorial/ReceiveLogs/bin/Debug/net8.0
/aleikin_artem_lab_4/ThirdTutorial/ReceiveLogs/obj
/aleikin_artem_lab_4/RVIPLab4.sln

View File

@ -1,118 +0,0 @@
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FastDeterminantCalculator {
public static void main(String[] args) {
int[] sizes = {100, 300, 500};
int[] threads = {1, 4, 8, 10};
for (int size : sizes) {
BigDecimal[][] matrix = generateMatrix(size);
for (int threadCount : threads) {
long start = System.currentTimeMillis();
BigDecimal determinant = calculateDeterminant(matrix, threadCount);
long end = System.currentTimeMillis();
System.out.printf("Matrix size: %dx%d, Threads: %d, Time: %d ms\n",
size, size, threadCount, (end - start));
}
}
}
public static BigDecimal[][] generateMatrix(int size) {
BigDecimal[][] matrix = new BigDecimal[size][size];
for (int i = 0; i < size; i++) {
BigDecimal rowSum = BigDecimal.ZERO;
for (int j = 0; j < size; j++) {
matrix[i][j] = BigDecimal.valueOf(Math.random() * 10);
if (i != j) {
rowSum = rowSum.add(matrix[i][j]);
}
}
matrix[i][i] = rowSum.add(BigDecimal.valueOf(Math.random() * 10 + 1));
}
return matrix;
}
public static BigDecimal calculateDeterminant(BigDecimal[][] matrix, int threadCount) {
int size = matrix.length;
BigDecimal[][] lu = new BigDecimal[size][size];
int[] permutations = new int[size];
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
if (!luDecomposition(matrix, lu, permutations, executor)) {
executor.shutdown();
return BigDecimal.ZERO; // Матрица вырожденная
}
executor.shutdown();
BigDecimal determinant = BigDecimal.ONE;
for (int i = 0; i < size; i++) {
determinant = determinant.multiply(lu[i][i]);
if (permutations[i] != i) {
determinant = determinant.negate(); // Меняем знак при перестановке
}
}
return determinant;
}
public static boolean luDecomposition(BigDecimal[][] matrix, BigDecimal[][] lu, int[] permutations, ExecutorService executor) {
int size = matrix.length;
for (int i = 0; i < size; i++) {
System.arraycopy(matrix[i], 0, lu[i], 0, size);
permutations[i] = i;
}
for (int k = 0; k < size; k++) {
int pivot = k;
for (int i = k + 1; i < size; i++) {
if (lu[i][k].abs().compareTo(lu[pivot][k].abs()) > 0) {
pivot = i;
}
}
if (lu[pivot][k].abs().compareTo(BigDecimal.valueOf(1e-10)) < 0) {
return false;
}
if (pivot != k) {
BigDecimal[] temp = lu[k];
lu[k] = lu[pivot];
lu[pivot] = temp;
int tempPerm = permutations[k];
permutations[k] = permutations[pivot];
permutations[pivot] = tempPerm;
}
CountDownLatch latch = new CountDownLatch(size - k - 1);
for (int i = k + 1; i < size; i++) {
int row = i;
int finalK = k;
executor.submit(() -> {
MathContext mc = new MathContext(20, RoundingMode.HALF_UP);
lu[row][finalK] = lu[row][finalK].divide(lu[finalK][finalK], mc);
for (int j = finalK + 1; j < size; j++) {
lu[row][j] = lu[row][j].subtract(lu[row][finalK].multiply(lu[finalK][j], mc));
}
latch.countDown();
});
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
return true;
}
}

View File

@ -1,31 +0,0 @@
# Лабораторная работа 6
## Описание
Задание заключается в реализации алгоритмов нахождения детерминанта квадратной матрицы. Необходимо разработать два алгоритма: последовательный и параллельный. А также провести бенчмарки, а затем описать результаты в отчете.
**100x100 матрица**:
- **8 потоков** — наилучший результат.
- **10 потоков** — результат немного хуже.
- **4 потока** — примерно такой же результат как на 10 потоках.
- **1 поток** — наихудший результат.
**300x300 матрица**:
- **10 потока** — лучший результат.
- **8 потоков** — чуть хуже.
- **4 потока** — ещё медленее.
- **1 поток** — наихудший результат.
**500x500 матрица**:
- **10 потока** — лучший результат.
- **8 потоков** — чуть хуже.
- **4 потока** — ещё медленее.
- **1 поток** — наихудший результат.
**Ссылка на демонстрацию работы программы**: https://vkvideo.ru/video215756667_456239456?list=ln-W6TTsYuIRdX8ft7ADr
**Вывод**:
- Если операция сложнее, рост производительности происходит с увеличением числа потоков.
- Слишком много потоков увеличивает накладные расходы (замтено только на неочень сложных операциях). Это может быть связано, например, с:
1. **Переключением контекстов**: Когда потоков больше, чем ядер процессора, операционная система часто переключает контексты, что занимает время.
2. **Конкуренцией за ресурсы**: Много потоков конкурируют за ограниченные ресурсы, такие как процессорное время и кэш.
3. **Управлением потоками**: С увеличением числа потоков растёт нагрузка на систему, связанную с их созданием, управлением и завершением.

View File

@ -1,13 +0,0 @@
Балансировка нагрузки — это способ распределения запросов между серверами для предотвращения их перегрузки и обеспечения быстродействия системы.
Для этого используются алгоритмы, такие как Round Robin, Least Connections, IP Hash.
Среди популярных открытых технологий — Nginx, HAProxy и Traefik. Nginx часто работает как реверс-прокси,
распределяя запросы и обрабатывая SSL. HAProxy подходит для высоконагруженных систем, а Traefik автоматически
настраивает маршрутизацию в облачных кластерах.
В базах данных балансировка нагрузки позволяет направлять запросы на чтение к репликам, а на запись — к основному узлу.
Инструменты, такие как ProxySQL, помогают автоматизировать этот процесс.
Реверс-прокси не только распределяет нагрузку, но и повышает безопасность системы, скрывая её внутреннюю архитектуру.
Таким образом, открытые технологии играют ключевую роль в создании масштабируемых и надёжных систем.

View File

@ -1,15 +0,0 @@
Распределённые системы являются основой современных сервисов, включая социальные сети. Их устройство предполагает разделение задач на микросервисы,
где каждый компонент выполняет узкоспециализированную функцию. Это упрощает разработку, позволяет масштабировать только необходимые части системы и
делает её более устойчивой к сбоям.
Для управления такими системами используются инструменты оркестрации, например, Kubernetes и Docker Swarm. Они автоматизируют развёртывание,
масштабирование и обновление сервисов, упрощая сопровождение. Однако их использование требует опыта и может осложнить отладку.
Очереди сообщений, такие как RabbitMQ или Kafka, помогают асинхронно передавать данные между сервисами. Это снижает нагрузку и обеспечивает надёжное взаимодействие,
передавая запросы, уведомления или данные для обработки.
Распределённые системы обладают преимуществами в виде масштабируемости, устойчивости и гибкости разработки.
Однако их сложность может стать серьёзным вызовом при проектировании и сопровождении.
Параллельные вычисления полезны, например, для обработки больших объёмов данных или машинного обучения,
но в некоторых случаях последовательная обработка более предпочтительна. Такой подход требует анализа задач, чтобы избежать излишней сложности.

View File

@ -1,30 +0,0 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@ -1,28 +0,0 @@
# См. статью по ссылке https://aka.ms/customizecontainer, чтобы узнать как настроить контейнер отладки и как Visual Studio использует этот Dockerfile для создания образов для ускорения отладки.
# Этот этап используется при запуске из VS в быстром режиме (по умолчанию для конфигурации отладки)
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base
USER app
WORKDIR /app
# Этот этап используется для сборки проекта службы
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Consumer1.csproj", "."]
RUN dotnet restore "./Consumer1.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./Consumer1.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Этот этап используется для публикации проекта службы, который будет скопирован на последний этап
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./Consumer1.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Этот этап используется в рабочей среде или при запуске из VS в обычном режиме (по умолчанию, когда конфигурация отладки не используется)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Consumer1.dll"]

View File

@ -1,39 +0,0 @@
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
var factory = new ConnectionFactory
{
HostName = "rabbitmq",
UserName = "admin",
Password = "admin"
};
using var connection = await factory.CreateConnectionAsync();
using var channel = await connection.CreateChannelAsync();
var queueName = "slow_queue";
var exchangeName = "logs_exchange";
await channel.QueueDeclareAsync(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
await channel.QueueBindAsync(queue: queueName, exchange: exchangeName, routingKey: "");
Console.WriteLine("[Consumer1] Waiting for messages...");
while (true)
{
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.ReceivedAsync += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($"[Consumer1] Received: {message}");
Thread.Sleep(new Random().Next(2000, 3000));
Console.WriteLine("[Consumer1] Done processing");
channel.BasicAckAsync(deliveryTag: ea.DeliveryTag, multiple: false);
return Task.CompletedTask;
};
await channel.BasicConsumeAsync(queue: queueName, autoAck: false, consumer: consumer);
}

View File

@ -1,10 +0,0 @@
{
"profiles": {
"Consumer1": {
"commandName": "Project"
},
"Container (Dockerfile)": {
"commandName": "Docker"
}
}
}

View File

@ -1,30 +0,0 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@ -1,28 +0,0 @@
# См. статью по ссылке https://aka.ms/customizecontainer, чтобы узнать как настроить контейнер отладки и как Visual Studio использует этот Dockerfile для создания образов для ускорения отладки.
# Этот этап используется при запуске из VS в быстром режиме (по умолчанию для конфигурации отладки)
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base
USER app
WORKDIR /app
# Этот этап используется для сборки проекта службы
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Consumer2.csproj", "."]
RUN dotnet restore "./Consumer2.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./Consumer2.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Этот этап используется для публикации проекта службы, который будет скопирован на последний этап
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./Consumer2.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Этот этап используется в рабочей среде или при запуске из VS в обычном режиме (по умолчанию, когда конфигурация отладки не используется)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Consumer2.dll"]

View File

@ -1,40 +0,0 @@
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Data.Common;
using System.Text;
using System.Threading.Channels;
var factory = new ConnectionFactory
{
HostName = "rabbitmq",
UserName = "admin",
Password = "admin"
};
using var connection = await factory.CreateConnectionAsync();
using var channel = await connection.CreateChannelAsync();
var queueName = "fast_queue";
var exchangeName = "logs_exchange";
await channel.QueueDeclareAsync(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
await channel.QueueBindAsync(queue: queueName, exchange: exchangeName, routingKey: "");
Console.WriteLine("[Consumer2] Waiting for messages...");
while (true)
{
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.ReceivedAsync += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($"[Consumer2] Received: {message}");
Console.WriteLine("[Consumer2] Done processing");
channel.BasicAckAsync(deliveryTag: ea.DeliveryTag, multiple: false);
return Task.CompletedTask;
};
await channel.BasicConsumeAsync(queue: queueName, autoAck: false, consumer: consumer);
}

View File

@ -1,10 +0,0 @@
{
"profiles": {
"Consumer2": {
"commandName": "Project"
},
"Container (Dockerfile)": {
"commandName": "Docker"
}
}
}

View File

@ -1,26 +0,0 @@
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = await factory.CreateConnectionAsync();
using var channel = await connection.CreateChannelAsync();
await channel.QueueDeclareAsync(queue: "hello", durable: false,
exclusive: false, autoDelete: false,arguments: null);
Console.WriteLine("[*] Waiting for messages...");
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.ReceivedAsync += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($" [*] Received {message}");
return Task.CompletedTask;
};
await channel.BasicConsumeAsync("hello", autoAck: true, consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -1,18 +0,0 @@
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = await factory.CreateConnectionAsync();
using var channel = await connection.CreateChannelAsync();
await channel.QueueDeclareAsync(queue: "hello", durable: false,
exclusive: false, autoDelete: false, arguments: null);
const string message = "Hello, World! ~from Artem";
var body = Encoding.UTF8.GetBytes(message);
await channel.BasicPublishAsync(exchange: string.Empty, routingKey: "hello", body: body);
Console.WriteLine($" [x] Sent {message}");
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
</ItemGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

View File

@ -1,30 +0,0 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@ -1,28 +0,0 @@
# См. статью по ссылке https://aka.ms/customizecontainer, чтобы узнать как настроить контейнер отладки и как Visual Studio использует этот Dockerfile для создания образов для ускорения отладки.
# Этот этап используется при запуске из VS в быстром режиме (по умолчанию для конфигурации отладки)
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base
USER app
WORKDIR /app
# Этот этап используется для сборки проекта службы
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Publisher.csproj", "."]
RUN dotnet restore "./Publisher.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./Publisher.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Этот этап используется для публикации проекта службы, который будет скопирован на последний этап
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./Publisher.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Этот этап используется в рабочей среде или при запуске из VS в обычном режиме (по умолчанию, когда конфигурация отладки не используется)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Publisher.dll"]

View File

@ -1,10 +0,0 @@
{
"profiles": {
"Publisher": {
"commandName": "Project"
},
"Container (Dockerfile)": {
"commandName": "Docker"
}
}
}

View File

@ -1,31 +0,0 @@
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory
{
HostName = "rabbitmq",
UserName = "admin",
Password = "admin"
};
using var connection = await factory.CreateConnectionAsync();
Console.WriteLine("Connection established.");
using var channel = await connection.CreateChannelAsync();
Console.WriteLine("Channel created.");
await channel.ExchangeDeclareAsync(exchange: "logs_exchange", type: ExchangeType.Fanout);
while (true)
{
var message = $"Event: {GenerateRandomEvent()}";
var body = Encoding.UTF8.GetBytes(message);
await channel.BasicPublishAsync(exchange: "logs_exchange", routingKey: string.Empty, body: body);
Console.WriteLine($"[Publisher] Sent: {message}");
await Task.Delay(1000);
}
static string GenerateRandomEvent()
{
var events = new[] { "Order Received", "User Message", "Create Report" };
return events[new Random().Next(events.Length)] + " #" + new Random().Next(0, 99);
}

View File

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>Container (Dockerfile)</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -1,26 +0,0 @@
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = await factory.CreateConnectionAsync();
using var channel = await connection.CreateChannelAsync();
await channel.QueueDeclareAsync(queue: "task_queue", durable: true, exclusive: false,
autoDelete: false, arguments: null);
var message = GetMessage(args);
var body = Encoding.UTF8.GetBytes(message);
var properties = new BasicProperties
{
Persistent = true
};
await channel.BasicPublishAsync(exchange: string.Empty, routingKey: "task_queue", mandatory: true,
basicProperties: properties, body: body);
Console.WriteLine($" [x] Sent {message}");
static string GetMessage(string[] args)
{
return ((args.Length > 0) ? string.Join(" ", args) : "Hello World!");
}

View File

@ -1,35 +0,0 @@
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = await factory.CreateConnectionAsync();
using var channel = await connection.CreateChannelAsync();
await channel.QueueDeclareAsync(queue: "task_queue", durable: true, exclusive: false,
autoDelete: false, arguments: null);
await channel.BasicQosAsync(prefetchSize: 0, prefetchCount: 1, global: false);
Console.WriteLine(" [*] Waiting for messages.");
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.ReceivedAsync += async (model, ea) =>
{
byte[] body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($" [x] Received {message}");
int dots = message.Split('.').Length - 1;
await Task.Delay(dots * 1000);
Console.WriteLine(" [x] Done");
// here channel could also be accessed as ((AsyncEventingBasicConsumer)sender).Channel
await channel.BasicAckAsync(deliveryTag: ea.DeliveryTag, multiple: false);
};
await channel.BasicConsumeAsync("task_queue", autoAck: false, consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -1,21 +0,0 @@
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = await factory.CreateConnectionAsync();
using var channel = await connection.CreateChannelAsync();
await channel.ExchangeDeclareAsync(exchange: "logs", type: ExchangeType.Fanout);
var message = GetMessage(args);
var body = Encoding.UTF8.GetBytes(message);
await channel.BasicPublishAsync(exchange: "logs", routingKey: string.Empty, body: body);
Console.WriteLine($" [x] Sent {message}");
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
static string GetMessage(string[] args)
{
return ((args.Length > 0) ? string.Join(" ", args) : "info: Hello World!");
}

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -1,31 +0,0 @@
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = await factory.CreateConnectionAsync();
using var channel = await connection.CreateChannelAsync();
await channel.ExchangeDeclareAsync(exchange: "logs",
type: ExchangeType.Fanout);
// declare a server-named queue
QueueDeclareOk queueDeclareResult = await channel.QueueDeclareAsync();
string queueName = queueDeclareResult.QueueName;
await channel.QueueBindAsync(queue: queueName, exchange: "logs", routingKey: string.Empty);
Console.WriteLine(" [*] Waiting for logs.");
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.ReceivedAsync += (model, ea) =>
{
byte[] body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($" [x] {message}");
return Task.CompletedTask;
};
await channel.BasicConsumeAsync(queueName, autoAck: true, consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@ -1,61 +0,0 @@
services:
rabbitmq:
image: rabbitmq:management
container_name: rabbitmq
restart: always
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: admin
RABBITMQ_DEFAULT_PASS: admin
networks:
- my_network
publisher:
build:
context: ./Publisher
restart: always
depends_on:
- rabbitmq
environment:
RABBITMQ_HOST: rabbitmq
RABBIT_USERNAME: admin
RABBIT_PASSWORD: admin
RABBIT_EXCHANGE: 'logs_exchange'
networks:
- my_network
consumer1:
build:
context: ./Consumer1
restart: always
depends_on:
- rabbitmq
environment:
RABBITMQ_HOST: rabbitmq
RABBIT_USERNAME: admin
RABBIT_PASSWORD: admin
RABBIT_EXCHANGE: 'logs_exchange'
RABBIT_QUEUE: 'slow_queue'
networks:
- my_network
consumer2:
build:
context: ./Consumer2
restart: always
depends_on:
- rabbitmq
environment:
RABBITMQ_HOST: rabbitmq
RABBIT_USERNAME: admin
RABBIT_PASSWORD: admin
RABBIT_EXCHANGE: 'logs_exchange'
RABBIT_QUEUE: 'fast_queue'
networks:
- my_network
networks:
my_network:
driver: bridge

View File

@ -1,37 +0,0 @@
# Лабораторная работа 4 - Работа с брокером сообщений
## ПИбд-42 || Алейкин Артем
### Описание
В данной лабораторной работе мы познакомились с такой утилитой как RabbitMQ.
### Туториалы
1. HelloWorld - Tutorial
![Консольный вывод - первый туториал](./Images/Туториал_1.png)
2. Work Queues - Tutorial
![Консольный вывод - второй туториал](./Images/Туториал_2.png)
3. Publish/Subscribe - Tutorial
![Консольный вывод - третий туториал](./Images/Туториал_3.png)
### Основное задание
Было разработано 3 приложения: Publisher, Consumer1 и Consumer2.
Первое отвечало за доставку сообщений в очереди. Оно генерирует одно сообщение раз в секунду.
Второе и Третье за обработку этих сообщений из очередей, но Consumer1 имел искусственную задержку в 2-3 секунды, в то время как Consumer2 таких ограничений не имел и работу.
### Шаги для запуска:
1. Запуск контейнеров:
```
docker-compose up -d
```
В результате мы можем посмотреть графики по этой ссылке http://localhost:15672/
![График Consumer1 - медленный](./Images/Лаба_Отчет2.png)
![График Consumer2 - быстрый](./Images/Лаба_Отчет1.png)
После этого было добавлено еще 3 клиента типа Consumer1 и только после этого их суммарной производительности стало хватать для обработки сообщений.
![График Consumer1 для нескольких клиентов - медленный](./Images/Лаба_Отчет3.png)
![График Consumer2 - быстрый](./Images/Лаба_Отчет4.png)
Видео демонстрации работы: https://vk.com/video248424990_456239611?list=ln-v0VkWDOiRBxdctENzV

View File

@ -1,10 +0,0 @@
# .gitignore
__pycache__/
*.pyc
*.pyo
*.pyd
*.db
*.log
*.bak
*.swp
*.swo

View File

@ -1,89 +0,0 @@
Для выполнения второй лабораторной работы по созданию распределённого приложения с использованием Docker и Docker Compose, давайте разберем все этапы, шаг за шагом. Я предлагаю реализовать вариант программы 1 и программу 2 следующим образом:
### 1. Вариант программы 1
Программа будет искать в каталоге `/var/data` файл с наибольшим количеством строк и перекладывать его в `/var/result/data.txt`.
### 2. Вариант программы 2
Программа будет искать наименьшее число из файла `/var/data/data.txt` и сохранять его третью степень в файл `/var/result/result.txt`.
### Структура проекта
1. `moiseev-vv-lab_2/worker-1`: Программа для нахождения файла с наибольшим количеством строк.
2. `moiseev-vv-lab_2/worker-2`: Программа для нахождения минимального числа в файле и записи его третьей степени.
### Шаги реализации:
#### 1. Реализация программы 1
```python
#### 2. Реализация программы 2
```python
Для обоих приложений создадим Dockerfile. Вот пример для **worker-1**:
Пояснение:
- **Stage 1**: Мы используем `python:3.10-slim` как образ для сборки, где копируем файл `main.py` и устанавливаем зависимости, если это необходимо.
- **Stage 2**: В этом слое мы копируем скомпилированные файлы из предыдущего этапа и определяем команду для запуска приложения.
Аналогичный Dockerfile будет для **worker-2**.
### Docker Compose файл
Теперь нужно настроить файл `docker-compose.yml`, который позволит запустить оба приложения:
Пояснение:
- **services**: Мы объявляем два сервиса — `worker-1` и `worker-2`.
- **build**: Указываем контекст сборки для каждого сервиса (директории, где находятся Dockerfile и код).
- **volumes**: Монтируем локальные директории `./data` и `./result` в контейнеры, чтобы обмениваться файлами между сервисами.
- **depends_on**: Задаем зависимость `worker-2` от `worker-1`, чтобы второй сервис запускался только после первого.
### .gitignore
Для предотвращения попадания ненужных файлов в репозиторий, добавляем файл `.gitignore`. Пример для Python проектов:
```
# .gitignore
__pycache__/
*.pyc
*.pyo
*.pyd
*.db
*.log
*.bak
*.swp
*.swo
```
### Шаги для сборки и запуска
1. Склонировать репозиторий и перейти в директорию с лабораторной работой:
```bash
git clone <репозиторий>
cd moiseev-vv-lab_2
```
2. Скопировать файлы для `worker-1` и `worker-2` в соответствующие папки.
3. Создать файл `docker-compose.yml`.
4. Запустить приложение с помощью команды:
```bash
docker-compose up --build
```
5. Проверить вывод, результаты должны быть в директориях `./data` и `./result`.
### Заключение
Это пример, как можно реализовать простейшее распределённое приложение с использованием Docker. Первое приложение генерирует данные для второго, который обрабатывает их и записывает результат в файл. Docker и Docker Compose позволяют легко управлять и изолировать каждое приложение.ker Compose для запуска двух программ, обрабатывающих данные в контейнерах.
## Видео ВК
https://vkvideo.ru/video150882239_456240341

View File

@ -1,7 +0,0 @@
FROM python:3.9-slim
WORKDIR /app
COPY . /app
CMD ["python", "generate_data.py"]

View File

@ -1,29 +0,0 @@
import os
import random
def generate_random_files(directory, num_files, num_lines_per_file, min_value, max_value):
os.makedirs(directory, exist_ok=True)
for i in range(num_files):
file_path = os.path.join(directory, f"file_{i + 1}.txt")
with open(file_path, 'w') as f:
for _ in range(num_lines_per_file):
random_number = random.randint(min_value, max_value)
f.write(f"{random_number}\n")
print(f"Generated file: {file_path}")
def main():
data_directory = '/var/data'
num_files = 10
num_lines_per_file = 12
min_value = 1
max_value = 100
generate_random_files(data_directory, num_files, num_lines_per_file, min_value, max_value)
print(f"Generated {num_files} files in {data_directory}")
if __name__ == "__main__":
main()

View File

@ -1,18 +0,0 @@
# docker-compose.yml
services:
worker-1:
build:
context: ./worker-1
volumes:
- ./data:/var/data
- ./result:/var/result
depends_on:
- worker-2
worker-2:
build:
context: ./worker-2
volumes:
- ./data:/var/data
- ./result:/var/result

View File

@ -1,14 +0,0 @@
# worker-1/Dockerfile
# Stage 1: Build the application
FROM python:3.10-slim as builder
WORKDIR /app
COPY ./main.py .
# Stage 2: Set up the runtime environment
FROM python:3.10-slim
WORKDIR /app
COPY --from=builder /app/main.py .
CMD ["python", "main.py"]

View File

@ -1,35 +0,0 @@
# worker-1/main.py
import os
def find_file_with_most_lines(directory):
files = os.listdir(directory)
max_lines = 0
target_file = None
for filename in files:
filepath = os.path.join(directory, filename)
if os.path.isfile(filepath):
with open(filepath, 'r') as file:
lines = file.readlines()
if len(lines) > max_lines:
max_lines = len(lines)
target_file = filepath
return target_file
def main():
source_directory = '/var/data'
result_file = '/var/result/data.txt'
file_to_copy = find_file_with_most_lines(source_directory)
if file_to_copy:
with open(file_to_copy, 'r') as source, open(result_file, 'w') as dest:
dest.writelines(source.readlines())
print(f"File with the most lines: {file_to_copy} copied to {result_file}")
else:
print("No files found in the source directory.")
if __name__ == "__main__":
main()

View File

@ -1,14 +0,0 @@
# worker-1/Dockerfile
# Stage 1: Build the application
FROM python:3.10-slim as builder
WORKDIR /app
COPY ./main.py .
# Stage 2: Set up the runtime environment
FROM python:3.10-slim
WORKDIR /app
COPY --from=builder /app/main.py .
CMD ["python", "main.py"]

View File

@ -1,23 +0,0 @@
# worker-2/main.py
def find_min_number(filename):
with open(filename, 'r') as file:
numbers = [int(line.strip()) for line in file.readlines()]
min_number = min(numbers)
return min_number
def main():
input_file = '/var/data/data.txt'
output_file = '/var/result/result.txt'
min_number = find_min_number(input_file)
result = min_number ** 3 # Cube of the minimum number
with open(output_file, 'w') as file:
file.write(str(result))
print(f"Minimum number's cube: {result} written to {output_file}")
if __name__ == "__main__":
main()

View File

@ -1,42 +0,0 @@
# Лабораторная работа №3: REST API, шлюз и синхронный обмен данными между микросервисами
## Задание
### Цель
Изучение принципов проектирования на основе паттерна шлюза, организации синхронного взаимодействия микросервисов и использования RESTful API.
### Основные задачи
1. Разработка двух микросервисов с поддержкой CRUD-операций для связанных сущностей.
2. Организация синхронного обмена данными между микросервисами.
3. Настройка шлюза на базе Nginx, выступающего в роли прозрачного прокси-сервера.
### Микросервисы
1. **hero_service** — микросервис для управления информацией о героях.
2. **item_service** — микросервис для обработки данных о предметах, принадлежащих героям.
### Связь между микросервисами
- Один герой (**hero**) может иметь множество связанных предметов (**items**) (соотношение 1:многие).
## Инструкция по запуску
Для запуска проекта выполните команду:
```bash
docker-compose up
```
## Описание работы
### Разработка микросервисов
Микросервисы разработаны с использованием языка программирования Python.
### Синхронное взаимодействие
`hero_service` обращается к `item_service` через HTTP-запросы для выполнения операций CRUD. Это позволяет получать актуальные данные о предметах, связанных с героями.
### Docker Compose
Файл `docker-compose.yml` описывает многоконтейнерное приложение, включающее три сервиса: `hero_service`, `item_service` и `nginx`. Nginx выполняет маршрутизацию запросов между сервисами.
### Nginx
Конфигурация Nginx задает параметры веб-сервера и обратного прокси, который принимает входящие запросы и направляет их к соответствующим микросервисам.
### Видеоматериал
Подробнее ознакомиться с проектом можно в видео:
https://vkvideo.ru/video150882239_456240342

View File

@ -1,26 +0,0 @@
version: '3.8'
services:
hero_service:
build:
context: ./hero_service
dockerfile: Dockerfile
ports:
- "5000:5000"
item_service:
build:
context: ./item_service
dockerfile: Dockerfile
ports:
- "5001:5001"
nginx:
image: nginx:latest
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- "80:80"
depends_on:
- hero_service
- item_service

View File

@ -1,10 +0,0 @@
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]

View File

@ -1,51 +0,0 @@
from flask import Flask, jsonify, request
import uuid
app = Flask(__name__)
heroes = {}
@app.route('/heroes', methods=['GET'])
def get_heroes():
return jsonify(list(heroes.values()))
@app.route('/heroes/<uuid:hero_uuid>', methods=['GET'])
def get_hero(hero_uuid):
hero = heroes.get(str(hero_uuid))
if hero:
return jsonify(hero)
return jsonify({'error': 'Not found'}), 404
@app.route('/heroes', methods=['POST'])
def create_hero():
data = request.get_json()
hero_uuid = str(uuid.uuid4())
hero = {
'uuid': hero_uuid,
'name': data['name'],
'role': data['role'],
'strength': data['strength']
}
heroes[hero_uuid] = hero
return jsonify(hero), 201
@app.route('/heroes/<uuid:hero_uuid>', methods=['PUT'])
def update_hero(hero_uuid):
hero = heroes.get(str(hero_uuid))
if not hero:
return jsonify({'error': 'Not found'}), 404
data = request.get_json()
hero['name'] = data['name']
hero['role'] = data['role']
hero['strength'] = data['strength']
return jsonify(hero)
@app.route('/heroes/<uuid:hero_uuid>', methods=['DELETE'])
def delete_hero(hero_uuid):
if str(hero_uuid) in heroes:
del heroes[str(hero_uuid)]
return '', 204
return jsonify({'error': 'Not found'}), 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

View File

@ -1 +0,0 @@
Flask

View File

@ -1,10 +0,0 @@
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]

View File

@ -1,51 +0,0 @@
from flask import Flask, jsonify, request
import uuid
app = Flask(__name__)
items = {}
@app.route('/items', methods=['GET'])
def get_items():
return jsonify(list(items.values()))
@app.route('/items/<uuid:item_uuid>', methods=['GET'])
def get_item(item_uuid):
item = items.get(str(item_uuid))
if item:
return jsonify(item)
return jsonify({'error': 'Not found'}), 404
@app.route('/items', methods=['POST'])
def create_item():
data = request.json
item_uuid = str(uuid.uuid4())
item = {
'uuid': item_uuid,
'name': data['name'],
'type': data['type'],
'hero_uuid': data['hero_uuid']
}
items[item_uuid] = item
return jsonify(item), 201
@app.route('/items/<uuid:item_uuid>', methods=['PUT'])
def update_item(item_uuid):
item = items.get(str(item_uuid))
if not item:
return jsonify({'error': 'Not found'}), 404
data = request.json
item['name'] = data['name']
item['type'] = data['type']
item['hero_uuid'] = data['hero_uuid']
return jsonify(item)
@app.route('/items/<uuid:item_uuid>', methods=['DELETE'])
def delete_item(item_uuid):
if str(item_uuid) in items:
del items[str(item_uuid)]
return '', 204
return jsonify({'error': 'Not found'}), 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)

View File

@ -1 +0,0 @@
Flask

View File

@ -1,11 +0,0 @@
server {
listen 80;
location /heroes {
proxy_pass http://hero_service:5000;
}
location /items {
proxy_pass http://item_service:5001;
}
}

View File

@ -1,36 +0,0 @@
# Лабораторная работа №5 ПИбд-42 Артамоновой Татьяны
## Запуск лабораторной работы
1. Установить библиотеки Python и NumPy
3. Запустить скрипт matrix.py с помощью команды: *python matrix.py*
## Используемые технологии
- Язык программирования: Python
- Библиотеки:
* numpy: Для работы с массивами и матрицами
* multiprocessing: Для параллельного выполнения кода
* time: Для измерения времени выполнения
## Задание на лабораторную работу
**Кратко:** реализовать умножение двух больших квадратных матриц.
**Подробно:** в лабораторной работе требуется сделать два алгоритма: обычный и параллельный (задание со * -
реализовать это в рамках одного алгоритма). В параллельном алгоритме предусмотреть ручное задание количества
потоков (число потоков = 1 как раз и реализует задание со *), каждый из которых будет выполнять умножение
элементов матрицы в рамках своей зоны ответственности.
## Результаты
![Результат работы](images/results.png)
## Вывод
Результаты показывают, что для маленьких матриц последовательное умножение быстрее.
Оптимальное количество потоков близко к количеству ядер процессора. Увеличение количества потоков сверх
оптимального значения не всегда ускоряет вычисления. Параллелизм эффективнее для больших матриц.
### [Видео](https://vk.com/video212084908_456239362)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

View File

@ -1,93 +0,0 @@
import time
import multiprocessing
import numpy as np
def multiply_matrices_sequential(matrix1, matrix2):
rows1 = len(matrix1)
cols1 = len(matrix1[0])
rows2 = len(matrix2)
cols2 = len(matrix2[0])
if cols1 != rows2:
raise ValueError("Число столбцов первой матрицы должно быть равно числу строк второй матрицы.")
result = [[0 for _ in range(cols2)] for _ in range(rows1)]
for i in range(rows1):
for j in range(cols2):
for k in range(cols1):
result[i][j] += matrix1[i][k] * matrix2[k][j]
return result
def multiply_matrices_parallel(matrix1, matrix2, num_processes):
rows1 = len(matrix1)
cols1 = len(matrix1[0])
rows2 = len(matrix2)
cols2 = len(matrix2[0])
if cols1 != rows2:
raise ValueError("Число столбцов первой матрицы должно быть равно числу строк второй матрицы.")
chunk_size = rows1 // num_processes
processes = []
results = []
with multiprocessing.Pool(processes=num_processes) as pool:
for i in range(num_processes):
start_row = i * chunk_size
end_row = (i + 1) * chunk_size if i < num_processes - 1 else rows1
p = pool.apply_async(multiply_matrix_chunk, (matrix1, matrix2, start_row, end_row))
processes.append(p)
for p in processes:
results.append(p.get())
result = [[0 for _ in range(cols2)] for _ in range(rows1)]
row_index = 0
for sub_result in results:
for row in sub_result:
result[row_index] = row
row_index += 1
return result
def multiply_matrix_chunk(matrix1, matrix2, start_row, end_row):
rows2 = len(matrix2)
cols2 = len(matrix2[0])
cols1 = len(matrix1[0])
result = [[0 for _ in range(cols2)] for _ in range(end_row - start_row)]
for i in range(end_row - start_row):
for j in range(cols2):
for k in range(cols1):
result[i][j] += matrix1[i + start_row][k] * matrix2[k][j]
return result
def benchmark(matrix_size, num_processes):
matrix1 = np.random.rand(matrix_size, matrix_size).tolist()
matrix2 = np.random.rand(matrix_size, matrix_size).tolist()
try:
start_time = time.time()
sequential_result = multiply_matrices_sequential(matrix1, matrix2)
end_time = time.time()
sequential_time = end_time - start_time
start_time = time.time()
parallel_result = multiply_matrices_parallel(matrix1, matrix2, num_processes)
end_time = time.time()
parallel_time = end_time - start_time
return sequential_time, parallel_time
except ValueError as e:
print(f"Ошибка бенчмарка с размером матрицы {matrix_size} и {num_processes} процессов: {e}")
return float('inf'), float('inf')
if __name__ == "__main__":
sizes = [100, 300, 500]
num_processes = int(input("Введите количество потоков: "))
print("Размер | Последовательно | Параллельно")
for size in sizes:
sequential_time, parallel_time = benchmark(size, num_processes)
print(f"{size:6} | {sequential_time:.4f} с \t | {parallel_time:.4f} с")

View File

@ -1,50 +0,0 @@
# Лабораторная работа №6 ПИбд-42 Артамоновой Татьяны
## Цель работы
Разработать и сравнить эффективность последовательного и
параллельного алгоритмов вычисления определителя квадратной матрицы.
## Методика
Для вычисления определителя использовался рекурсивный алгоритм, основанный
на разложении по первой строке. Параллельная версия алгоритма реализована
с помощью библиотеки multiprocessing, разделяя вычисление алгебраических
дополнений между несколькими процессами. Время выполнения каждого алгоритма
замерялось для матриц различных размеров (100x100, 300x300, 500x500).
Эксперименты проводились с различным количеством потоков (1, 2, 4).
## Результаты
Результаты бенчмарка представлены на изображении:
[Фото](images/result.png)
## Анализ результатов
Результаты демонстрируют неоднозначную эффективность параллельного подхода.
Для матрицы 100x100 параллельные версии работают медленнее, чем последовательная.
Это объясняется значительными накладными расходами на создание и синхронизацию
процессов, которые преобладают над выигрышем от распараллеливания для небольших
задач.
Для матриц 300x300 и 500x500 наблюдается неожиданное поведение: параллельные
версии работают значительно медленнее, чем последовательная. Это указывает на
наличие проблем в реализации параллельного алгоритма. Скорее всего, проблема
связана с неэффективным использованием multiprocessing и значительными накладными
расходами на межпроцессное взаимодействие, которые перевешивают выигрыш от
распараллеливания. Рекурсивный алгоритм вычисления детерминанта плохо
масштабируется при распараллеливании, так как большая часть времени тратится
на рекурсивные вызовы, которые не могут быть эффективно распределены между
процессами.
## Выводы
Полученные результаты показывают, что для выбранного рекурсивного
алгоритма и способа распараллеливания, параллельная реализация не эффективна.
Для эффективного использования параллельных вычислений необходимы более
подходящие алгоритмы вычисления определителя, например, алгоритмы, основанные
на LU-разложении, а также более тщательная оптимизация распараллеливания с
учетом накладных расходов. В данном случае, последовательный алгоритм оказался
быстрее для всех размеров матриц, кроме 100х100, где разница незначительна.
### [Видео](https://vk.com/video212084908_456239363)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

View File

@ -1,62 +0,0 @@
import numpy as np
import time
import threading
def determinant_sequential(matrix):
return np.linalg.det(matrix)
def determinant_parallel(matrix, num_threads):
n = len(matrix)
if n == 1:
return matrix[0][0]
if n == 2:
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]
threads = []
results = [0] * num_threads
chunk_size = n // num_threads
def worker(thread_id):
start_index = thread_id * chunk_size
end_index = min((thread_id + 1) * chunk_size, n)
det_sum = 0
for i in range(start_index, end_index):
submatrix = np.delete(matrix, i, 0)
submatrix = np.delete(submatrix, 0, 1)
det_sum += (-1)**i * matrix[i][0] * determinant_sequential(submatrix)
results[thread_id] = det_sum
for i in range(num_threads):
thread = threading.Thread(target=worker, args=(i,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
return sum(results)
sizes = [100, 300, 500]
num_threads = [1, 2, 4]
results = {}
for size in sizes:
matrix = np.random.rand(size, size)
results[size] = {}
for n_threads in num_threads:
start_time = time.time()
if n_threads == 1:
det = determinant_sequential(matrix)
else:
det = determinant_parallel(matrix, n_threads)
end_time = time.time()
results[size][n_threads] = end_time - start_time
print(f"Размер матрицы: {size}x{size}, Потоков: {n_threads}, Время: {end_time - start_time:.4f} сек.")
print("\n## Результаты бенчмарка:")
print("| Размер матрицы | 1 поток (последовательно) | 2 потока | 4 потока |")
for size, timings in results.items():
print(f"| {size}x{size} | {timings [1] :.4f} сек. | {timings.get(2, 'N/A'):.4f} сек. | {timings.get(4, 'N/A'):.4f} сек. |")

View File

@ -1,66 +0,0 @@
## Лабораторная работа №7
### Эссе на тему "Балансировка нагрузки в распределённых системах при помощи открытых технологий на примерах"
При масштабировании приложения появляется необходимость в
добавлении новых серверов. В таком случае нужно пользоваться
балансировщиком нагрузок для распределения запросов.
А что делать, если серверов больше сотни или тысячи?
Балансировка нагрузки позволяет обеспечить равномерное распределение
нагрузки между несколькими серверами. Без неё один сервер может
перегрузиться, а другой простаивать. Это приводит к неэффективной
работе системы.
Для решения этой проблемы используют различные алгоритмы.
1. **"Round-robin"** - балансировка нагрузок циклическим перебором.
Алгоритм можно описать так: балансировщик нагрузок отправляет
запрос каждому серверу по очереди. Это решает проблему "отбрасывания"
входящих запросов, когда предыдущий запрос еще не был обработан.
Такой алгоритм подойдет для серверов с одинаковой мощностью и
одинаково затратных запросов.
2. **Очереди запросов** - балансировка нагрузок с помощью создания очередей
запросов.
Редко встречаются ситуации, когда все запросы одинаково обрабатываются.
Очереди запросов позволяют справиться с этой проблемой, но со своими
недостатками.
"Отбрасываться" будет меньше запросов, но некоторые из них будут иметь
задержку при обработке (так как будут ожидать в очереди)
3. **"Weighted round-robin"** - взвешенный цикличный перебор.
Алгоритм заключается в назначении веса каждому серверу. Вес определяет,
сколько запросов сможет обработать сервер. Это нужно, для подстраивания
к мощности каждого сервера.
В качестве открытых технологий для балансировки нагрузки используются **HAProxy** и **Nginx**.
Они выступают в роли обратного прокси-сервера: принимают запросы от клиентов
и перенаправляют их на один из доступных серверов. Они предлагают
гибкую настройку алгоритмов балансировки и отслеживания состояния серверов.
Кроме того, существуют решения на основе Kubernetes или других
оркестраторов контейнеров, которые управляют распределением нагрузки
по микросервисам.
Балансировка нагрузки на базах данных — специфическая задача, решаемая
с помощью механизмов репликации и кластеризации.
**Репликация** создает копии данных на нескольких серверах. Это обеспечивает
отказоустойчивость и повышение производительности за счет распределения
нагрузки чтения.
**Кластеризация** объединяет несколько баз данных в единую систему. Это позволяет
распределять нагрузку записи и чтения между несколькими базами.
Выбор оптимального подхода зависит от специфики приложения
и требований к доступности и производительности.
**Реверс-прокси** играет ключевую роль в балансировке нагрузки.
Он принимает все входящие запросы, контролирует доступность серверов
на бэке и перенаправляет запросы на доступные и наименее загруженные сервера.
Кроме распределения нагрузки, реверс-прокси выполняет другие важные функции:
кэширование, SSL-терминирование и защита от атак. Благодаря своей гибкости
и функциональности, реверс-прокси является важным компонентом
высокопроизводительных и отказоустойчивых распределенных систем.

View File

@ -1,37 +0,0 @@
## Лабораторная работа №8 ПИбд-42 Артамоновой Татьяны
### Эссе на тему "Устройство распределенных систем"
Сложные системы строятся по принципу распределенных приложений,
где каждый сервис отвечает за свою узкую специализацию. Это повышает масштабируемость,
надежность и удобство разработки. Разделение на отдельные сервисы позволяет независимо
масштабировать отдельные компоненты системы. Это упростит их обновление, замену и
повышает отказоустойчивость: выход из строя одного сервиса не обязательно
парализует всю систему.
Системы оркестрации, такие, как Kubernetes, были созданы для упрощения управления и
масштабирования распределенных систем. Они автоматизируют развертывание, масштабирование и
мониторинг приложений, работающих в контейнерах. С одной стороны, оркестраторы значительно
упрощают разработку, предоставляя готовые инструменты для управления инфраструктурой.
С другой стороны, они добавляют сложность требуется освоение новых инструментов и концепций,
а сама система оркестрации может стать узким местом.
Очереди сообщений (RabbitMQ, Kafka) используются для асинхронной передачи данных
между сервисами. Сообщения могут представлять собой любые данные: запросы на выполнение
действия, уведомления о событиях, результаты обработки. Асинхронная обработка позволяет
повысить производительность и отказоустойчивость, так как сервисы не зависят от мгновенного
ответа друг друга. Если один сервис временно недоступен, сообщения накапливаются в очереди и
обрабатываются позже.
Преимущества распределенных приложений очевидны: масштабируемость, отказоустойчивость,
гибкость разработки. Но есть и недостатки. Распределенная система гораздо сложнее в разработке,
тестировании и отладке, чем монолитная. Возникают проблемы с согласованностью данных,
распределёнными транзакциями и мониторингом.
Внедрение параллельных вычислений в сложную распределенную систему имеет смысл,
если есть задачи, которые можно разбить на независимые подзадачи, решаемые параллельно.
Например, обработка больших объемов данных или выполнение ресурсоемких
вычислений. Однако, если система ограничена I/O операциями,
параллельные вычисления могут не дать значительного выигрыша
в производительности из-за "узких мест" в других частях системы. В таких случаях,
оптимизация I/O операций может быть эффективнее, чем добавление параллелизма.

View File

@ -1,56 +0,0 @@
## Эссе на тему "Балансировка нагрузки в распределённых системах при помощи открытых технологий на примерах"
# Балансировка нагрузки: алгоритмы, методы и технологии
Балансировка нагрузки — это процесс распределения входящего трафика или вычислительных задач между несколькими серверами, чтобы обеспечить равномерное использование ресурсов, минимизировать задержки и повысить отказоустойчивость системы.
---
## Алгоритмы и методы
Для реализации балансировки нагрузки используются различные алгоритмы:
- **Round Robin** — циклическое распределение запросов между серверами.
- **Least Connections** — запрос направляется на сервер с наименьшим количеством активных соединений.
- **Weighted Round Robin** — циклическое распределение с учётом веса серверов.
- **Dynamic Load Balancing** — алгоритмы, учитывающие текущую нагрузку и производительность серверов.
---
## Открытые технологии
Существует множество технологий, поддерживающих балансировку нагрузки:
- **Nginx** и **HAProxy** — мощные и популярные инструменты для HTTP и TCP трафика.
- **Kubernetes Ingress Controller** — автоматическая балансировка нагрузки в контейнерных средах.
- **AWS Elastic Load Balancer** и **Google Cloud Load Balancer** — решения для облачных инфраструктур.
---
## Балансировка нагрузки на базах данных
Балансировка базы данных требует специальных подходов:
- **Репликация**: создание копий базы данных для чтения, чтобы распределить запросы.
- **Шардинг**: разделение данных на сегменты, хранящиеся на разных серверах.
- Использование инструментов:
- **ProxySQL** для MySQL
- **pgpool-II** для PostgreSQL
---
## Реверс-прокси как элемент балансировки
Реверс-прокси выполняет роль промежуточного сервера:
- Принимает запросы от клиентов и перенаправляет их на бэкэнд-сервера.
- Обеспечивает:
- Распределение нагрузки
- Кеширование
- Шифрование (SSL/TLS)
- Мониторинг и анализ трафика
Популярные решения:
- **Nginx**
- **Traefik**
---
## Заключение
Балансировка нагрузки — это не только необходимая мера для обеспечения стабильности высоконагруженных систем, но и мощный инструмент для их масштабирования и оптимизации. Выбор алгоритмов и технологий зависит от особенностей проекта и его требований.

View File

@ -1,66 +0,0 @@
# Как Вы поняли, что называется распределенной системой и как она устроена?
# Распределенные системы: принципы, инструменты и подходы
Современные сложные системы, такие как социальные сети и крупные веб-приложения, часто строятся как распределенные. Это позволяет обеспечить их масштабируемость, отказоустойчивость и эффективность. В этом README рассматриваются основные аспекты распределённых систем.
---
## Зачем системы пишутся в распределенном стиле?
В распределённых системах каждое приложение (или сервис) выполняет ограниченный спектр задач. Это позволяет:
- **Разделить ответственность**: каждый сервис фокусируется на своей задаче, что упрощает разработку и сопровождение.
- **Обеспечить масштабируемость**: можно увеличивать мощность отдельных сервисов без необходимости модификации всей системы.
- **Повысить отказоустойчивость**: сбой одного сервиса не приводит к остановке всей системы.
- Пример: в социальной сети ВКонтакте отдельные сервисы отвечают за отправку сообщений, хранение медиафайлов, обработку запросов API и рекламу.
---
## Для чего нужны системы оркестрации?
Системы оркестрации, такие как **Kubernetes** или **Docker Swarm**, были созданы для управления контейнеризированными приложениями. Они:
- **Упрощают разработку**: обеспечивают автоматическое масштабирование, балансировку нагрузки и перезапуск сервисов.
- **Упрощают сопровождение**: помогают управлять зависимостями, обеспечивать резервное копирование и отслеживать состояние системы.
- **Усложняют начальную настройку**: требуют знания инфраструктуры и инструментов, но окупаются на стадии эксплуатации.
Пример: в крупной системе оркестрация позволяет автоматически перераспределять ресурсы между сервисами в зависимости от их нагрузки.
---
## Зачем нужны очереди обработки сообщений?
Очереди сообщений (например, **RabbitMQ**, **Kafka**) используются для обмена данными между сервисами. Под сообщениями могут подразумеваться:
- Запросы на выполнение задач.
- Уведомления об изменении состояния.
- Данные для обработки (например, лог-файлы или транзакции).
Очереди:
- **Развязывают взаимодействие сервисов**: отправитель и получатель могут работать независимо.
- **Обеспечивают устойчивость**: сохраняют сообщения до их обработки.
---
## Преимущества и недостатки распределённых приложений
### Преимущества:
- Масштабируемость.
- Устойчивость к сбоям.
- Локализация изменений (меньше шансов нарушить работу всей системы).
### Недостатки:
- Увеличенная сложность разработки.
- Зависимость от сетевой инфраструктуры (задержки, потеря данных).
- Требуются дополнительные инструменты для мониторинга и управления.
---
## Параллельные вычисления в распределенных системах
### Когда это необходимо:
- **Обработка больших данных**: анализ логов, машинное обучение, трансляции видео.
- **Высоконагруженные системы**: расчёт рекомендаций, обновление новостных лент в реальном времени.
### Когда это избыточно:
- Малые системы с низкой нагрузкой.
- Простые веб-приложения, где вычисления не требуют значительных ресурсов.
---
## Заключение
Распределенные системы и связанные с ними инструменты играют важную роль в построении современных приложений. Однако их применение оправдано только там, где есть высокая нагрузка или необходимость в масштабировании. Успех разработки таких систем зависит от правильного выбора архитектуры, инструментов и методов разработки.

View File

@ -1,2 +0,0 @@
node_modules/
*.log

View File

@ -1,55 +0,0 @@
# Лабораторная работа 2 - Разработка простейшего распределённого приложения
### ПИбд-42 || Бондаренко Максим
# Описание работы
> Задача:
В данной лабораторной работе изучить способы создания и развертывания простого распределённого приложения.
### Первая программа лабораторной работы.
```
Вариант - 2: Формирует файл /var/result/data.txt из первых строк всех файлов каталога /var/data.
Для реализации программы я буду использовать JavaScript с Node.js
```
### Вторая программа лабораторной работы.
```
Вариант - 2: Ищет наименьшее число из файла /var/data/data.txt и сохраняет его третью степень в /var/result/result.txt.
Для реализации программы я буду использовать JavaScript с Node.js
```
> Инструкция по запуску
1. Запуск Docker - Desktop
2. Открыть консоль в папке с docker-compose.yml
3. Ввести команду:
```
docker-compose up --build
```
> Docker-compose.yml
```
version: '3'
services:
app1:
build:
context: ./app-1
volumes:
- ./data:/var/data # Монтируем локальную папку data в /var/data
- ./result:/var/result # Монтируем локальную папку result в /var/result
container_name: app1
app2:
build:
context: ./app-2
depends_on:
- app1
volumes:
- ./result:/var/result # Монтируем ту же папку result
container_name: app2
```
#### В результате в папке result создаётся два текстовых документа:
1. data - результат работы первого проекта
2. result - результат работы второго проекта
> Видео демонстрация работы
https://cloud.mail.ru/public/FyyE/LTkRXBQXN

View File

@ -1,14 +0,0 @@
# Образ с Node.js версии 18
FROM node:18
# Создание директории
WORKDIR /app
# Добавление кода в контейнер из index.js
COPY index.js .
# Установка рабочей папки для вводных данных и результата
RUN mkdir -p /var/data /var/result
# Запуск index.js с помощью среды выполнения Node.js
CMD ["node", "index.js"]

View File

@ -1,33 +0,0 @@
// Первая программа лабораторной работы.
// Вариант - 2: Формирует файл /var/result/data.txt из первых строк всех файлов каталога /var/data.
// Для реализации программы я буду использовать JavaScript с Node.js
// Импорт модулей
const fs = require('fs');
const path = require('path');
// Добавляем пути к папкам
const dataDir = '/var/data';
const resultFile = '/var/result/data.txt';
// Функция для извлечения первых строк всех файлов в data и записи резултата в result
const gatherFirstLines = () => {
// Обёртываю в try/catch
try {
const files = fs.readdirSync(dataDir); // Считывание название файлов из data
// Проходимся по файлам
// Добовляем первые строчки
const firstLines = files.map((file) => {
const filePath = path.join(dataDir, file);
const content = fs.readFileSync(filePath, 'utf-8');
return content.split('\n')[0];
});
fs.writeFileSync(resultFile, firstLines.join('\n')); // Записываем первые строки в `data.txt`
console.log('First lines have been successfully written to', resultFile); // Логирую в терминал результат
} catch (error) {
console.error('Error processing files:', error); // Перехватываю ошибку
}
};
gatherFirstLines(); // Вызываю функцию

View File

@ -1,14 +0,0 @@
# Образ с Node.js версии 18
FROM node:18
# Создание директории
WORKDIR /app
# Добавление кода в контейнер из index.js
COPY index.js .
# Установка рабочей папки для вводных данных и результата
RUN mkdir -p /var/data /var/result
# Запуск index.js с помощью среды выполнения Node.js
CMD ["node", "index.js"]

View File

@ -1,30 +0,0 @@
// Вторая программа лабораторной работы.
// Вариант - 2: Ищет наименьшее число из файла /var/data/data.txt и сохраняет его третью степень в /var/result/result.txt.
// Для реализации программы я буду использовать JavaScript с Node.js
// Импорт модулей
const fs = require('fs');
const path = require('path');
// Добавляем пути к папкам
const dataFile = '/var/result/data.txt';
const resultFile = '/var/result/result.txt';
// Функция для запаси наименьшего числа в кубе из data в result
const calculateMinCubed = () => {
// Обёртываю в try/catch
try {
const content = fs.readFileSync(dataFile, 'utf-8'); // Открытие файла с кодировкой UTF-8
const numbers = content.split('\n').map(Number).filter(Boolean); // Преобразую в числа
const minNumber = Math.min(...numbers); // Ищу минимальное число
const result = Math.pow(minNumber, 3); // Возвожу в 3-тью степень
fs.writeFileSync(resultFile, result.toString()); // Записываю результат в файл
console.log(`Cubed minimum number (${minNumber}^3) saved to`, resultFile); // Логирую в терминал результат
} catch (error) {
console.error('Error processing file:', error); // Перехватываю ошибку
}
};
calculateMinCubed(); // Вызываю функцию

View File

@ -1,10 +0,0 @@
25
91
77
63
45
25
21
89
6
18

View File

@ -1,10 +0,0 @@
98
62
45
77
65
45
61
62
10
76

View File

@ -1,10 +0,0 @@
37
54
100
34
1
77
55
10
30
28

View File

@ -1,18 +0,0 @@
version: '3'
services:
app1:
build:
context: ./app-1
volumes:
- ./data:/var/data # Монтируем локальную папку data в /var/data
- ./result:/var/result # Монтируем локальную папку result в /var/result
container_name: app1
app2:
build:
context: ./app-2
depends_on:
- app1
volumes:
- ./result:/var/result # Монтируем ту же папку result
container_name: app2

View File

@ -1,108 +0,0 @@
# Лабораторная работа 3 - REST API, Gateway и синхронный обмен между микросервисами
### ПИбд-42 || Бондаренко Максим
> Цель:
Изучение шаблона проектирования gateway, построения синхронного обмена между микросервисами и архитектурного стиля RESTful API.
> Задачи:
1. Создать 2 микросервиса, реализующих CRUD на связанных сущностях.
2. Реализовать механизм синхронного обмена сообщениями между микросервисами.
3. Реализовать шлюз на основе прозрачного прокси-сервера nginx.
> Описание
В данной лабораторной работе мы разворачиваем два микросервиса:
1. Authors - микросервис для управления авторами.
2. Books - микросервис для управления книгами.
> Файлы-конфигурации
1. docker-compose.yml
```
version: '3.7'
services:
authors:
build:
context: ./authors
ports:
- "3000:3000"
networks:
- app-network
books:
build:
context: ./books
ports:
- "3001:3001"
networks:
- app-network
depends_on:
- authors
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- authors
- books
networks:
- app-network
networks:
app-network:
driver: bridge
```
authors: Микросервис, отвечающий за управление данными об авторах, доступный на порту 3000.
books: Микросервис, отвечающий за управление данными о книгах, доступный на порту 3001. Зависит от микросервиса authors.
nginx: Сервис, работающий как обратный прокси-сервер, маршрутизирующий запросы к другим сервисам authors и books, слушая на порту 80.
2. nginx.conf
```
events {
worker_connections 1024;
}
http {
upstream authors_service {
server authors:3000;
}
upstream books_service {
server books:3001;
}
server {
listen 80;
location /authors {
proxy_pass http://authors_service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /books {
proxy_pass http://books_service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
```
location /authors: Все запросы, начинающиеся с /authors, будут проксироваться к микросервису authors, который работает на порту 3000.
location /books: Все запросы, начинающиеся с /books, будут проксироваться к микросервису books, который работает на порту 3001.
### Шаги для запуска:
Переходим в корневую папку всего решения и пишем команду:
```
docker-compose up --build
```
> Видео демонстрация работы
https://cloud.mail.ru/public/hLWs/iuqweU92e

View File

@ -1 +0,0 @@
PORT=3000

View File

@ -1,20 +0,0 @@
# Используем базовый образ Node.js
FROM node:14
# Создаем рабочую директорию в контейнере
WORKDIR /app
# Копируем package.json и package-lock.json
COPY package*.json ./
# Устанавливаем зависимости
RUN npm install
# Копируем исходный код
COPY . .
# Указываем порт, который будет слушать приложение
EXPOSE 3000
# Команда для запуска приложения
CMD ["npm", "start"]

View File

@ -1,14 +0,0 @@
{
"name": "authors",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js"
},
"dependencies": {
"express": "^4.17.1",
"swagger-jsdoc": "^6.1.0",
"swagger-ui-express": "^4.3.0",
"uuid": "^8.3.2"
}
}

View File

@ -1,50 +0,0 @@
const { Author, authors } = require('../models/authorModel');
const getAuthors = (req, res) => {
res.json(authors);
};
const getAuthorById = (req, res) => {
const author = authors.find(a => a.uuid === req.params.uuid);
if (author) {
res.json(author);
} else {
res.status(404).send('Author not found');
}
};
const createAuthor = (req, res) => {
const { name, birthdate } = req.body;
const newAuthor = new Author(name, birthdate);
authors.push(newAuthor);
res.status(201).json(newAuthor);
};
const updateAuthor = (req, res) => {
const author = authors.find(a => a.uuid === req.params.uuid);
if (author) {
author.name = req.body.name || author.name;
author.birthdate = req.body.birthdate || author.birthdate;
res.json(author);
} else {
res.status(404).send('Author not found');
}
};
const deleteAuthor = (req, res) => {
const authorIndex = authors.findIndex(a => a.uuid === req.params.uuid);
if (authorIndex !== -1) {
authors.splice(authorIndex, 1);
res.status(204).send();
} else {
res.status(404).send('Author not found');
}
};
module.exports = {
getAuthors,
getAuthorById,
createAuthor,
updateAuthor,
deleteAuthor
};

View File

@ -1,15 +0,0 @@
const express = require('express');
const bodyParser = require('body-parser');
const authorRoutes = require('./routes/authorRoutes');
const setupSwagger = require('./swagger');
const app = express();
const port = process.env.PORT || 3000;
app.use(bodyParser.json());
app.use('/authors', authorRoutes);
setupSwagger(app);
app.listen(port, () => {
console.log(`Authors service is running on port ${port}`);
});

View File

@ -1,16 +0,0 @@
const { v4: uuidv4 } = require('uuid');
class Author {
constructor(name, birthdate) {
this.uuid = uuidv4();
this.name = name;
this.birthdate = birthdate;
}
}
const authors = [];
module.exports = {
Author,
authors
};

View File

@ -1,166 +0,0 @@
const express = require('express');
const {
getAuthors,
getAuthorById,
createAuthor,
updateAuthor,
deleteAuthor
} = require('../controllers/authorController');
const router = express.Router();
/**
* @swagger
* components:
* schemas:
* Author:
* type: object
* required:
* - name
* - birthdate
* properties:
* uuid:
* type: string
* description: The auto-generated UUID of the author
* name:
* type: string
* description: The name of the author
* birthdate:
* type: string
* format: date
* description: The birthdate of the author
* example:
* uuid: d5fE_asz
* name: J.K. Rowling
* birthdate: 1965-07-31
*/
/**
* @swagger
* tags:
* name: Authors
* description: The authors managing API
*/
/**
* @swagger
* /authors:
* get:
* summary: Returns the list of all the authors
* tags: [Authors]
* responses:
* 200:
* description: The list of the authors
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/Author'
*/
router.get('/', getAuthors);
/**
* @swagger
* /authors/{uuid}:
* get:
* summary: Get the author by id
* tags: [Authors]
* parameters:
* - in: path
* name: uuid
* schema:
* type: string
* required: true
* description: The author id
* responses:
* 200:
* description: The author description by id
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Author'
* 404:
* description: The author was not found
*/
router.get('/:uuid', getAuthorById);
/**
* @swagger
* /authors:
* post:
* summary: Create a new author
* tags: [Authors]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Author'
* responses:
* 201:
* description: The author was successfully created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Author'
* 500:
* description: Some server error
*/
router.post('/', createAuthor);
/**
* @swagger
* /authors/{uuid}:
* put:
* summary: Update the author by the id
* tags: [Authors]
* parameters:
* - in: path
* name: uuid
* schema:
* type: string
* required: true
* description: The author id
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Author'
* responses:
* 200:
* description: The author was updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Author'
* 404:
* description: The author was not found
* 500:
* description: Some error happened
*/
router.put('/:uuid', updateAuthor);
/**
* @swagger
* /authors/{uuid}:
* delete:
* summary: Remove the author by id
* tags: [Authors]
* parameters:
* - in: path
* name: uuid
* schema:
* type: string
* required: true
* description: The author id
* responses:
* 204:
* description: The author was deleted
* 404:
* description: The author was not found
*/
router.delete('/:uuid', deleteAuthor);
module.exports = router;

View File

@ -1,22 +0,0 @@
const swaggerJSDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Authors API',
version: '1.0.0',
description: 'API for managing authors',
},
},
apis: ['./src/routes/*.js'],
};
const swaggerSpec = swaggerJSDoc(options);
const setupSwagger = (app) => {
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
};
module.exports = setupSwagger;

View File

@ -1 +0,0 @@
PORT=3001

View File

@ -1,20 +0,0 @@
# Используем базовый образ Node.js
FROM node:14
# Создаем рабочую директорию в контейнере
WORKDIR /app
# Копируем package.json и package-lock.json
COPY package*.json ./
# Устанавливаем зависимости
RUN npm install
# Копируем исходный код
COPY . .
# Указываем порт, который будет слушать приложение
EXPOSE 3001
# Команда для запуска приложения
CMD ["npm", "start"]

View File

@ -1,16 +0,0 @@
{
"name": "books",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js"
},
"dependencies": {
"express": "^4.17.1",
"swagger-jsdoc": "^6.1.0",
"swagger-ui-express": "^4.3.0",
"axios": "^0.21.1",
"uuid": "^8.3.2"
}
}

View File

@ -1,72 +0,0 @@
const axios = require('axios');
const { Book, books } = require('../models/bookModel');
const getBooks = (req, res) => {
res.json(books);
};
const getBookById = async (req, res) => {
const book = books.find(b => b.uuid === req.params.uuid);
if (book) {
try {
const authorResponse = await axios.get(`http://authors:3000/authors/${book.authorUuid}`);
book.authorInfo = authorResponse.data;
res.json(book);
} catch (error) {
res.status(500).send('Error fetching author information');
}
} else {
res.status(404).send('Book not found');
}
};
const createBook = async (req, res) => {
const { author, title, year, authorUuid } = req.body;
try {
const authorResponse = await axios.get(`http://authors:3000/authors/${authorUuid}`);
if (!authorResponse.data) {
return res.status(404).send('Author not found');
}
const newBook = new Book(author, title, year, authorUuid);
books.push(newBook);
res.status(201).json(newBook);
} catch (error) {
if (error.response && error.response.status === 404) {
res.status(404).send('Author not found');
} else {
res.status(500).send('Error creating book');
}
}
};
const updateBook = (req, res) => {
const book = books.find(b => b.uuid === req.params.uuid);
if (book) {
book.author = req.body.author || book.author;
book.title = req.body.title || book.title;
book.year = req.body.year || book.year;
book.authorUuid = req.body.authorUuid || book.authorUuid;
res.json(book);
} else {
res.status(404).send('Book not found');
}
};
const deleteBook = (req, res) => {
const bookIndex = books.findIndex(b => b.uuid === req.params.uuid);
if (bookIndex !== -1) {
books.splice(bookIndex, 1);
res.status(204).send();
} else {
res.status(404).send('Book not found');
}
};
module.exports = {
getBooks,
getBookById,
createBook,
updateBook,
deleteBook
};

View File

@ -1,15 +0,0 @@
const express = require('express');
const bodyParser = require('body-parser');
const bookRoutes = require('./routes/bookRoutes');
const setupSwagger = require('./swagger');
const app = express();
const port = process.env.PORT || 3001;
app.use(bodyParser.json());
app.use('/books', bookRoutes);
setupSwagger(app);
app.listen(port, () => {
console.log(`Books service is running on port ${port}`);
});

View File

@ -1,18 +0,0 @@
const { v4: uuidv4 } = require('uuid');
class Book {
constructor(author, title, year, authorUuid) {
this.uuid = uuidv4();
this.author = author;
this.title = title;
this.year = year;
this.authorUuid = authorUuid;
}
}
const books = [];
module.exports = {
Book,
books
};

View File

@ -1,175 +0,0 @@
const express = require('express');
const {
getBooks,
getBookById,
createBook,
updateBook,
deleteBook
} = require('../controllers/bookController');
const router = express.Router();
/**
* @swagger
* components:
* schemas:
* Book:
* type: object
* required:
* - author
* - title
* - year
* - authorUuid
* properties:
* uuid:
* type: string
* description: The auto-generated UUID of the book
* author:
* type: string
* description: The author of the book
* title:
* type: string
* description: The title of the book
* year:
* type: integer
* description: The year the book was published
* authorUuid:
* type: string
* description: The UUID of the author
* example:
* uuid: d5fE_asz
* author: J.K. Rowling
* title: Harry Potter
* year: 1997
* authorUuid: d5fE_asz
*/
/**
* @swagger
* tags:
* name: Books
* description: The books managing API
*/
/**
* @swagger
* /books:
* get:
* summary: Returns the list of all the books
* tags: [Books]
* responses:
* 200:
* description: The list of the books
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/Book'
*/
router.get('/', getBooks);
/**
* @swagger
* /books/{uuid}:
* get:
* summary: Get the book by id
* tags: [Books]
* parameters:
* - in: path
* name: uuid
* schema:
* type: string
* required: true
* description: The book id
* responses:
* 200:
* description: The book description by id
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Book'
* 404:
* description: The book was not found
*/
router.get('/:uuid', getBookById);
/**
* @swagger
* /books:
* post:
* summary: Create a new book
* tags: [Books]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Book'
* responses:
* 201:
* description: The book was successfully created
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Book'
* 500:
* description: Some server error
*/
router.post('/', createBook);
/**
* @swagger
* /books/{uuid}:
* put:
* summary: Update the book by the id
* tags: [Books]
* parameters:
* - in: path
* name: uuid
* schema:
* type: string
* required: true
* description: The book id
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Book'
* responses:
* 200:
* description: The book was updated
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Book'
* 404:
* description: The book was not found
* 500:
* description: Some error happened
*/
router.put('/:uuid', updateBook);
/**
* @swagger
* /books/{uuid}:
* delete:
* summary: Remove the book by id
* tags: [Books]
* parameters:
* - in: path
* name: uuid
* schema:
* type: string
* required: true
* description: The book id
* responses:
* 204:
* description: The book was deleted
* 404:
* description: The book was not found
*/
router.delete('/:uuid', deleteBook);
module.exports = router;

View File

@ -1,22 +0,0 @@
const swaggerJSDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Books API',
version: '1.0.0',
description: 'API for managing books',
},
},
apis: ['./src/routes/*.js'],
};
const swaggerSpec = swaggerJSDoc(options);
const setupSwagger = (app) => {
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
};
module.exports = setupSwagger;

View File

@ -1,36 +0,0 @@
version: '3.7'
services:
authors:
build:
context: ./authors
ports:
- "3000:3000"
networks:
- app-network
books:
build:
context: ./books
ports:
- "3001:3001"
networks:
- app-network
depends_on:
- authors
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- authors
- books
networks:
- app-network
networks:
app-network:
driver: bridge

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