Compare commits
6 Commits
main
...
kurushina_
Author | SHA1 | Date | |
---|---|---|---|
06485f2473 | |||
|
a00d32694b | ||
|
e83e9844f7 | ||
|
effeb9b8cd | ||
|
19819d2a15 | ||
|
b99aa9c5ac |
57
.gitignore
vendored
@ -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
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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. **Управлением потоками**: С увеличением числа потоков растёт нагрузка на систему, связанную с их созданием, управлением и завершением.
|
|
@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
Балансировка нагрузки — это способ распределения запросов между серверами для предотвращения их перегрузки и обеспечения быстродействия системы.
|
|
||||||
Для этого используются алгоритмы, такие как Round Robin, Least Connections, IP Hash.
|
|
||||||
|
|
||||||
Среди популярных открытых технологий — Nginx, HAProxy и Traefik. Nginx часто работает как реверс-прокси,
|
|
||||||
распределяя запросы и обрабатывая SSL. HAProxy подходит для высоконагруженных систем, а Traefik автоматически
|
|
||||||
настраивает маршрутизацию в облачных кластерах.
|
|
||||||
|
|
||||||
В базах данных балансировка нагрузки позволяет направлять запросы на чтение к репликам, а на запись — к основному узлу.
|
|
||||||
Инструменты, такие как ProxySQL, помогают автоматизировать этот процесс.
|
|
||||||
|
|
||||||
Реверс-прокси не только распределяет нагрузку, но и повышает безопасность системы, скрывая её внутреннюю архитектуру.
|
|
||||||
Таким образом, открытые технологии играют ключевую роль в создании масштабируемых и надёжных систем.
|
|
@ -1,15 +0,0 @@
|
|||||||
Распределённые системы являются основой современных сервисов, включая социальные сети. Их устройство предполагает разделение задач на микросервисы,
|
|
||||||
где каждый компонент выполняет узкоспециализированную функцию. Это упрощает разработку, позволяет масштабировать только необходимые части системы и
|
|
||||||
делает её более устойчивой к сбоям.
|
|
||||||
|
|
||||||
Для управления такими системами используются инструменты оркестрации, например, Kubernetes и Docker Swarm. Они автоматизируют развёртывание,
|
|
||||||
масштабирование и обновление сервисов, упрощая сопровождение. Однако их использование требует опыта и может осложнить отладку.
|
|
||||||
|
|
||||||
Очереди сообщений, такие как RabbitMQ или Kafka, помогают асинхронно передавать данные между сервисами. Это снижает нагрузку и обеспечивает надёжное взаимодействие,
|
|
||||||
передавая запросы, уведомления или данные для обработки.
|
|
||||||
|
|
||||||
Распределённые системы обладают преимуществами в виде масштабируемости, устойчивости и гибкости разработки.
|
|
||||||
Однако их сложность может стать серьёзным вызовом при проектировании и сопровождении.
|
|
||||||
|
|
||||||
Параллельные вычисления полезны, например, для обработки больших объёмов данных или машинного обучения,
|
|
||||||
но в некоторых случаях последовательная обработка более предпочтительна. Такой подход требует анализа задач, чтобы избежать излишней сложности.
|
|
@ -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/**
|
|
@ -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>
|
|
@ -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>
|
|
@ -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"]
|
|
@ -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);
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"profiles": {
|
|
||||||
"Consumer1": {
|
|
||||||
"commandName": "Project"
|
|
||||||
},
|
|
||||||
"Container (Dockerfile)": {
|
|
||||||
"commandName": "Docker"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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/**
|
|
@ -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>
|
|
@ -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>
|
|
@ -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"]
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"profiles": {
|
|
||||||
"Consumer2": {
|
|
||||||
"commandName": "Project"
|
|
||||||
},
|
|
||||||
"Container (Dockerfile)": {
|
|
||||||
"commandName": "Docker"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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();
|
|
@ -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>
|
|
@ -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();
|
|
@ -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>
|
|
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 147 KiB |
Before Width: | Height: | Size: 69 KiB |
@ -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/**
|
|
@ -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"]
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"profiles": {
|
|
||||||
"Publisher": {
|
|
||||||
"commandName": "Project"
|
|
||||||
},
|
|
||||||
"Container (Dockerfile)": {
|
|
||||||
"commandName": "Docker"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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!");
|
|
||||||
}
|
|
@ -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();
|
|
@ -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>
|
|
@ -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!");
|
|
||||||
}
|
|
@ -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>
|
|
@ -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();
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
10
alkin_ivan_lab_2/.gitignore
vendored
@ -1,10 +0,0 @@
|
|||||||
# .gitignore
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*.pyd
|
|
||||||
*.db
|
|
||||||
*.log
|
|
||||||
*.bak
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
@ -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
|
|
@ -1,7 +0,0 @@
|
|||||||
FROM python:3.9-slim
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY . /app
|
|
||||||
|
|
||||||
CMD ["python", "generate_data.py"]
|
|
@ -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()
|
|
@ -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
|
|
@ -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"]
|
|
@ -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()
|
|
@ -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"]
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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"]
|
|
@ -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)
|
|
@ -1 +0,0 @@
|
|||||||
Flask
|
|
@ -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"]
|
|
@ -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)
|
|
@ -1 +0,0 @@
|
|||||||
Flask
|
|
@ -1,11 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
|
|
||||||
location /heroes {
|
|
||||||
proxy_pass http://hero_service:5000;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /items {
|
|
||||||
proxy_pass http://item_service:5001;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
Before Width: | Height: | Size: 107 KiB |
@ -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} с")
|
|
@ -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)
|
|
Before Width: | Height: | Size: 79 KiB |
@ -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} сек. |")
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
|||||||
## Лабораторная работа №7
|
|
||||||
|
|
||||||
### Эссе на тему "Балансировка нагрузки в распределённых системах при помощи открытых технологий на примерах"
|
|
||||||
|
|
||||||
При масштабировании приложения появляется необходимость в
|
|
||||||
добавлении новых серверов. В таком случае нужно пользоваться
|
|
||||||
балансировщиком нагрузок для распределения запросов.
|
|
||||||
|
|
||||||
А что делать, если серверов больше сотни или тысячи?
|
|
||||||
|
|
||||||
Балансировка нагрузки позволяет обеспечить равномерное распределение
|
|
||||||
нагрузки между несколькими серверами. Без неё один сервер может
|
|
||||||
перегрузиться, а другой простаивать. Это приводит к неэффективной
|
|
||||||
работе системы.
|
|
||||||
|
|
||||||
Для решения этой проблемы используют различные алгоритмы.
|
|
||||||
|
|
||||||
1. **"Round-robin"** - балансировка нагрузок циклическим перебором.
|
|
||||||
Алгоритм можно описать так: балансировщик нагрузок отправляет
|
|
||||||
запрос каждому серверу по очереди. Это решает проблему "отбрасывания"
|
|
||||||
входящих запросов, когда предыдущий запрос еще не был обработан.
|
|
||||||
Такой алгоритм подойдет для серверов с одинаковой мощностью и
|
|
||||||
одинаково затратных запросов.
|
|
||||||
|
|
||||||
2. **Очереди запросов** - балансировка нагрузок с помощью создания очередей
|
|
||||||
запросов.
|
|
||||||
Редко встречаются ситуации, когда все запросы одинаково обрабатываются.
|
|
||||||
Очереди запросов позволяют справиться с этой проблемой, но со своими
|
|
||||||
недостатками.
|
|
||||||
"Отбрасываться" будет меньше запросов, но некоторые из них будут иметь
|
|
||||||
задержку при обработке (так как будут ожидать в очереди)
|
|
||||||
|
|
||||||
3. **"Weighted round-robin"** - взвешенный цикличный перебор.
|
|
||||||
Алгоритм заключается в назначении веса каждому серверу. Вес определяет,
|
|
||||||
сколько запросов сможет обработать сервер. Это нужно, для подстраивания
|
|
||||||
к мощности каждого сервера.
|
|
||||||
|
|
||||||
В качестве открытых технологий для балансировки нагрузки используются **HAProxy** и **Nginx**.
|
|
||||||
Они выступают в роли обратного прокси-сервера: принимают запросы от клиентов
|
|
||||||
и перенаправляют их на один из доступных серверов. Они предлагают
|
|
||||||
гибкую настройку алгоритмов балансировки и отслеживания состояния серверов.
|
|
||||||
Кроме того, существуют решения на основе Kubernetes или других
|
|
||||||
оркестраторов контейнеров, которые управляют распределением нагрузки
|
|
||||||
по микросервисам.
|
|
||||||
|
|
||||||
Балансировка нагрузки на базах данных — специфическая задача, решаемая
|
|
||||||
с помощью механизмов репликации и кластеризации.
|
|
||||||
|
|
||||||
**Репликация** создает копии данных на нескольких серверах. Это обеспечивает
|
|
||||||
отказоустойчивость и повышение производительности за счет распределения
|
|
||||||
нагрузки чтения.
|
|
||||||
|
|
||||||
**Кластеризация** объединяет несколько баз данных в единую систему. Это позволяет
|
|
||||||
распределять нагрузку записи и чтения между несколькими базами.
|
|
||||||
Выбор оптимального подхода зависит от специфики приложения
|
|
||||||
и требований к доступности и производительности.
|
|
||||||
|
|
||||||
**Реверс-прокси** играет ключевую роль в балансировке нагрузки.
|
|
||||||
Он принимает все входящие запросы, контролирует доступность серверов
|
|
||||||
на бэке и перенаправляет запросы на доступные и наименее загруженные сервера.
|
|
||||||
Кроме распределения нагрузки, реверс-прокси выполняет другие важные функции:
|
|
||||||
кэширование, SSL-терминирование и защита от атак. Благодаря своей гибкости
|
|
||||||
и функциональности, реверс-прокси является важным компонентом
|
|
||||||
высокопроизводительных и отказоустойчивых распределенных систем.
|
|
||||||
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
|||||||
## Лабораторная работа №8 ПИбд-42 Артамоновой Татьяны
|
|
||||||
|
|
||||||
### Эссе на тему "Устройство распределенных систем"
|
|
||||||
|
|
||||||
Сложные системы строятся по принципу распределенных приложений,
|
|
||||||
где каждый сервис отвечает за свою узкую специализацию. Это повышает масштабируемость,
|
|
||||||
надежность и удобство разработки. Разделение на отдельные сервисы позволяет независимо
|
|
||||||
масштабировать отдельные компоненты системы. Это упростит их обновление, замену и
|
|
||||||
повышает отказоустойчивость: выход из строя одного сервиса не обязательно
|
|
||||||
парализует всю систему.
|
|
||||||
|
|
||||||
Системы оркестрации, такие, как Kubernetes, были созданы для упрощения управления и
|
|
||||||
масштабирования распределенных систем. Они автоматизируют развертывание, масштабирование и
|
|
||||||
мониторинг приложений, работающих в контейнерах. С одной стороны, оркестраторы значительно
|
|
||||||
упрощают разработку, предоставляя готовые инструменты для управления инфраструктурой.
|
|
||||||
С другой стороны, они добавляют сложность – требуется освоение новых инструментов и концепций,
|
|
||||||
а сама система оркестрации может стать узким местом.
|
|
||||||
|
|
||||||
Очереди сообщений (RabbitMQ, Kafka) используются для асинхронной передачи данных
|
|
||||||
между сервисами. Сообщения могут представлять собой любые данные: запросы на выполнение
|
|
||||||
действия, уведомления о событиях, результаты обработки. Асинхронная обработка позволяет
|
|
||||||
повысить производительность и отказоустойчивость, так как сервисы не зависят от мгновенного
|
|
||||||
ответа друг друга. Если один сервис временно недоступен, сообщения накапливаются в очереди и
|
|
||||||
обрабатываются позже.
|
|
||||||
|
|
||||||
Преимущества распределенных приложений очевидны: масштабируемость, отказоустойчивость,
|
|
||||||
гибкость разработки. Но есть и недостатки. Распределенная система гораздо сложнее в разработке,
|
|
||||||
тестировании и отладке, чем монолитная. Возникают проблемы с согласованностью данных,
|
|
||||||
распределёнными транзакциями и мониторингом.
|
|
||||||
|
|
||||||
Внедрение параллельных вычислений в сложную распределенную систему имеет смысл,
|
|
||||||
если есть задачи, которые можно разбить на независимые подзадачи, решаемые параллельно.
|
|
||||||
Например, обработка больших объемов данных или выполнение ресурсоемких
|
|
||||||
вычислений. Однако, если система ограничена I/O операциями,
|
|
||||||
параллельные вычисления могут не дать значительного выигрыша
|
|
||||||
в производительности из-за "узких мест" в других частях системы. В таких случаях,
|
|
||||||
оптимизация I/O операций может быть эффективнее, чем добавление параллелизма.
|
|
@ -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**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Заключение
|
|
||||||
Балансировка нагрузки — это не только необходимая мера для обеспечения стабильности высоконагруженных систем, но и мощный инструмент для их масштабирования и оптимизации. Выбор алгоритмов и технологий зависит от особенностей проекта и его требований.
|
|
@ -1,66 +0,0 @@
|
|||||||
# Как Вы поняли, что называется распределенной системой и как она устроена?
|
|
||||||
|
|
||||||
# Распределенные системы: принципы, инструменты и подходы
|
|
||||||
|
|
||||||
Современные сложные системы, такие как социальные сети и крупные веб-приложения, часто строятся как распределенные. Это позволяет обеспечить их масштабируемость, отказоустойчивость и эффективность. В этом README рассматриваются основные аспекты распределённых систем.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Зачем системы пишутся в распределенном стиле?
|
|
||||||
В распределённых системах каждое приложение (или сервис) выполняет ограниченный спектр задач. Это позволяет:
|
|
||||||
- **Разделить ответственность**: каждый сервис фокусируется на своей задаче, что упрощает разработку и сопровождение.
|
|
||||||
- **Обеспечить масштабируемость**: можно увеличивать мощность отдельных сервисов без необходимости модификации всей системы.
|
|
||||||
- **Повысить отказоустойчивость**: сбой одного сервиса не приводит к остановке всей системы.
|
|
||||||
- Пример: в социальной сети ВКонтакте отдельные сервисы отвечают за отправку сообщений, хранение медиафайлов, обработку запросов API и рекламу.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Для чего нужны системы оркестрации?
|
|
||||||
Системы оркестрации, такие как **Kubernetes** или **Docker Swarm**, были созданы для управления контейнеризированными приложениями. Они:
|
|
||||||
- **Упрощают разработку**: обеспечивают автоматическое масштабирование, балансировку нагрузки и перезапуск сервисов.
|
|
||||||
- **Упрощают сопровождение**: помогают управлять зависимостями, обеспечивать резервное копирование и отслеживать состояние системы.
|
|
||||||
- **Усложняют начальную настройку**: требуют знания инфраструктуры и инструментов, но окупаются на стадии эксплуатации.
|
|
||||||
Пример: в крупной системе оркестрация позволяет автоматически перераспределять ресурсы между сервисами в зависимости от их нагрузки.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Зачем нужны очереди обработки сообщений?
|
|
||||||
Очереди сообщений (например, **RabbitMQ**, **Kafka**) используются для обмена данными между сервисами. Под сообщениями могут подразумеваться:
|
|
||||||
- Запросы на выполнение задач.
|
|
||||||
- Уведомления об изменении состояния.
|
|
||||||
- Данные для обработки (например, лог-файлы или транзакции).
|
|
||||||
|
|
||||||
Очереди:
|
|
||||||
- **Развязывают взаимодействие сервисов**: отправитель и получатель могут работать независимо.
|
|
||||||
- **Обеспечивают устойчивость**: сохраняют сообщения до их обработки.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Преимущества и недостатки распределённых приложений
|
|
||||||
|
|
||||||
### Преимущества:
|
|
||||||
- Масштабируемость.
|
|
||||||
- Устойчивость к сбоям.
|
|
||||||
- Локализация изменений (меньше шансов нарушить работу всей системы).
|
|
||||||
|
|
||||||
### Недостатки:
|
|
||||||
- Увеличенная сложность разработки.
|
|
||||||
- Зависимость от сетевой инфраструктуры (задержки, потеря данных).
|
|
||||||
- Требуются дополнительные инструменты для мониторинга и управления.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Параллельные вычисления в распределенных системах
|
|
||||||
|
|
||||||
### Когда это необходимо:
|
|
||||||
- **Обработка больших данных**: анализ логов, машинное обучение, трансляции видео.
|
|
||||||
- **Высоконагруженные системы**: расчёт рекомендаций, обновление новостных лент в реальном времени.
|
|
||||||
|
|
||||||
### Когда это избыточно:
|
|
||||||
- Малые системы с низкой нагрузкой.
|
|
||||||
- Простые веб-приложения, где вычисления не требуют значительных ресурсов.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Заключение
|
|
||||||
Распределенные системы и связанные с ними инструменты играют важную роль в построении современных приложений. Однако их применение оправдано только там, где есть высокая нагрузка или необходимость в масштабировании. Успех разработки таких систем зависит от правильного выбора архитектуры, инструментов и методов разработки.
|
|
2
bondarenko_max_lab_2/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
*.log
|
|
@ -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
|
|
@ -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"]
|
|
@ -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(); // Вызываю функцию
|
|
@ -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"]
|
|
@ -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(); // Вызываю функцию
|
|
@ -1,10 +0,0 @@
|
|||||||
25
|
|
||||||
91
|
|
||||||
77
|
|
||||||
63
|
|
||||||
45
|
|
||||||
25
|
|
||||||
21
|
|
||||||
89
|
|
||||||
6
|
|
||||||
18
|
|
@ -1,10 +0,0 @@
|
|||||||
98
|
|
||||||
62
|
|
||||||
45
|
|
||||||
77
|
|
||||||
65
|
|
||||||
45
|
|
||||||
61
|
|
||||||
62
|
|
||||||
10
|
|
||||||
76
|
|
@ -1,10 +0,0 @@
|
|||||||
37
|
|
||||||
54
|
|
||||||
100
|
|
||||||
34
|
|
||||||
1
|
|
||||||
77
|
|
||||||
55
|
|
||||||
10
|
|
||||||
30
|
|
||||||
28
|
|
@ -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
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||||||
PORT=3000
|
|
@ -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"]
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
};
|
|
@ -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}`);
|
|
||||||
});
|
|
@ -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
|
|
||||||
};
|
|
@ -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;
|
|
@ -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;
|
|
@ -1 +0,0 @@
|
|||||||
PORT=3001
|
|
@ -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"]
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
};
|
|
@ -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}`);
|
|
||||||
});
|
|
@ -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
|
|
||||||
};
|
|
@ -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;
|
|
@ -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;
|
|
@ -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
|
|