Как распределить вычисления в ASP.NET и docker?
Я пытаюсь создать на ASP.NET распределенную систему, которая вычисляет простое число в определенном диапазоне с помощью вероятностного алгоритма. У меня есть центральный узел и 3 рабочих узла, центральный узел должен распределить диапазоны для рабочих узлов, а они будут производить вычисления. Но я не до конца понимаю, как это все запустить на докере. У меня есть контейнер, который включает в себя 4 образа (узлы). Но при запуске не отображается порт. И возможно надо было создать 4 контейнера вместо 4 образов. Приложил скриншоты и код.
Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["DistributedSystems_Lab1.csproj", ""]
RUN dotnet restore "./DistributedSystems_Lab1.csproj"
COPY . .
RUN dotnet publish "DistributedSystems_Lab1.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "DistributedSystems_Lab1.dll"]
Docker-compose.yml:
version: '3.8'
services:
master:
image: my_app:latest
ports:
- "8080:8080"
deploy:
replicas: 1
restart_policy:
condition: on-failure
worker:
image: my_app:latest
deploy:
replicas: 3
restart_policy:
condition: on-failure
PrimalityTest.cs:
namespace DistributedSystems_Lab1
{
using System.Numerics;
public static class PrimalityTest
{
public static bool IsPrime(BigInteger number, int k = 5)
{
if (number < 2) return false;
if (number == 2 || number == 3) return true;
if (number % 2 == 0) return false;
BigInteger d = number - 1;
int r = 0;
while (d % 2 == 0)
{
d /= 2;
r++;
}
Random rand = new();
for (int i = 0; i < k; i++)
{
BigInteger a = RandomBigInteger(2, number - 2, rand);
BigInteger x = BigInteger.ModPow(a, d, number);
if (x == 1 || x == number - 1) continue;
for (int j = 0; j < r - 1; j++)
{
x = BigInteger.ModPow(x, 2, number);
if (x == number - 1) break;
}
if (x != number - 1) return false;
}
return true;
}
private static BigInteger RandomBigInteger(BigInteger min, BigInteger max, Random rand)
{
byte[] bytes = new byte[max.ToByteArray().Length];
BigInteger result;
do
{
rand.NextBytes(bytes);
result = new BigInteger(bytes);
} while (result < min || result > max);
return result;
}
}
}
PrimeCheckRequest.cs:
namespace DistributedSystems_Lab1
{
public class PrimeCheckRequest()
{
public string Start { get; set; }
public string End { get; set; }
}
}
PrimeCheckResponse.cs:
namespace DistributedSystems_Lab1
{
public class PrimeCheckResponse
{
public List<string> Primes { get; set; } = new();
}
}
PrimeController.cs:
namespace DistributedSystems_Lab1
{
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Numerics;
[ApiController]
[Route("api/[controller]")]
public class PrimeController : ControllerBase
{
[HttpPost("check")]
public ActionResult<PrimeCheckResponse> CheckPrimes([FromBody] PrimeCheckRequest request)
{
BigInteger start = BigInteger.Parse(request.Start);
BigInteger end = BigInteger.Parse(request.End);
List<string> primes = new();
for (BigInteger i = start; i <= end; i++)
{
if (PrimalityTest.IsPrime(i))
{
primes.Add(i.ToString());
}
}
return new PrimeCheckResponse { Primes = primes };
}
}
}
Program.cs:
using DistributedSystems_Lab1;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using System.Net.Http.Json;
using System.Numerics;
using System.Text.Json;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Определяем роль: мастер или воркер
string role = Environment.GetEnvironmentVariable("ROLE") ?? "worker";
if (role == "master")
{
// Маршрут для отправки задач воркерам
app.MapPost("/api/master/start", async (HttpContext context) =>
{
var request = await context.Request.ReadFromJsonAsync<PrimeCheckRequest>();
if (request == null) return Results.BadRequest("Invalid request");
BigInteger start = BigInteger.Parse(request.Start);
BigInteger end = BigInteger.Parse(request.End);
int workerCount = 3;
BigInteger step = (end - start) / workerCount;
List<Task<List<string>>> tasks = new();
// Распределяем диапазоны между воркерами
for (int i = 0; i < workerCount; i++)
{
BigInteger subStart = start + i * step;
BigInteger subEnd = (i == workerCount - 1) ? end : subStart + step;
tasks.Add(SendToWorker(subStart, subEnd));
}
// Собираем результаты
var results = await Task.WhenAll(tasks);
var primes = results.SelectMany(x => x).ToList();
return Results.Json(new PrimeCheckResponse { Primes = primes });
});
}
else
{
// Воркер выполняет проверку чисел
app.MapPost("/api/worker/check", async (HttpContext context) =>
{
var request = await context.Request.ReadFromJsonAsync<PrimeCheckRequest>();
if (request == null) return Results.BadRequest("Invalid request");
BigInteger start = BigInteger.Parse(request.Start);
BigInteger end = BigInteger.Parse(request.End);
List<string> primes = new();
for (BigInteger i = start; i <= end; i++)
{
if (PrimalityTest.IsPrime(i)) primes.Add(i.ToString());
}
return Results.Json(new PrimeCheckResponse { Primes = primes });
});
}
app.Run();
// Функция отправки задач воркерам
static async Task<List<string>> SendToWorker(BigInteger start, BigInteger end)
{
using var client = new HttpClient();
var workerUrls = new[] { "http://worker1:80", "http://worker2:80", "http://worker3:80" };
var randomWorker = workerUrls[new Random().Next(workerUrls.Length)];
var response = await client.PostAsJsonAsync($"{randomWorker}/api/worker/check", new PrimeCheckRequest
{
Start = start.ToString(),
End = end.ToString()
});
var result = await response.Content.ReadFromJsonAsync<PrimeCheckResponse>();
return result?.Primes ?? new List<string>();
}
Источник: Stack Overflow на русском