Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0a769f7
feat: добавил ServiceDefaults
Donistr Feb 22, 2026
69e6dba
feat: добавил сервис Generator
Donistr Feb 22, 2026
9b66e86
fix: добавил файлы, которые забыл закоммитить
Donistr Feb 22, 2026
4eaaee0
feat: добавил AppHost
Donistr Feb 22, 2026
52dc141
fix: поправил в клиенте карточку студента и сделал чтобы url получени…
Donistr Feb 22, 2026
70c225b
style: запустил code cleanup во всём проекте
Donistr Feb 22, 2026
ae35609
refactor: вынес faker в private static readonly поле
Donistr Feb 22, 2026
8724252
style: сделал revert коммита с клинапом
Donistr Feb 25, 2026
6cab0cf
fix: убрал serilog, чтобы пофиксить структурное логирование
Donistr Feb 25, 2026
c6f84a5
refactor: убрал метод CreateCahcheOptions
Donistr Feb 25, 2026
4c1f771
refactor: улучшил логирование сгенерированного объекта ResidentialBui…
Donistr Feb 25, 2026
51fac4e
fix: поправил cors
Donistr Feb 25, 2026
38f2929
refactor: убрал объявление бесполезной переменной generator
Donistr Feb 25, 2026
fbc4abf
reafactor: исправил порядок импортов
Donistr Feb 26, 2026
7712171
refactor: добавил в контроллер атрибуты с возвращаемыми типами
Donistr Feb 26, 2026
9d30440
fix: изменил клиент под gateway и под 2 лабу
Donistr Mar 4, 2026
3369278
fix: изменил генератор под query параметр
Donistr Mar 4, 2026
ad90a89
feat: добавил ocelot с кастомным quary based балансировщиком нагрузки
Donistr Mar 4, 2026
b900ae7
fix: изменил AppHost под ocelot
Donistr Mar 4, 2026
4208170
fix: убрал бесполезный lock из балансировщика
Donistr Mar 5, 2026
3191a83
refactor: сделал в AppHost.cs создание генераторов через цикл
Donistr Mar 5, 2026
1c6352f
refactor: убрал бесполезный cors у сервиса-генератора
Donistr Mar 5, 2026
8f9d89b
refactor: убрал из конфига оцелота плейсхолдеры из маршрутов
Donistr Mar 5, 2026
472fee8
fix: исправил балансировщик, чтобы он соответствовал заданию
Donistr Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Client.Wasm/Components/StudentCard.razor
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
</CardHeader>
<CardBody>
<UnorderedList Unstyled>
<UnorderedListItem>Номер <Strong>№X "Название лабораторной"</Strong></UnorderedListItem>
<UnorderedListItem>Вариант <Strong>№Х "Название варианта"</Strong></UnorderedListItem>
<UnorderedListItem>Выполнена <Strong>Фамилией Именем 65ХХ</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://puginarug.com/">Ссылка на форк</Link></UnorderedListItem>
<UnorderedListItem>Номер <Strong>№2 «Балансировка нагрузки»</Strong></UnorderedListItem>
<UnorderedListItem>Вариант <Strong>№38 "Query Based"</Strong></UnorderedListItem>
<UnorderedListItem>Выполнена <Strong>Елагиным Денисом 6513</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://github.com/Donistr/cloud-development">Ссылка на форк</Link></UnorderedListItem>
</UnorderedList>
</CardBody>
</Card>
2 changes: 1 addition & 1 deletion Client.Wasm/wwwroot/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
}
},
"AllowedHosts": "*",
"BaseAddress": ""
"BaseAddress": "http://localhost:5300/residential-building"
}
24 changes: 24 additions & 0 deletions CloudDevelopment.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ VisualStudioVersion = 17.14.36811.4
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client.Wasm", "Client.Wasm\Client.Wasm.csproj", "{AE7EEA74-2FE0-136F-D797-854FD87E022A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResidentialBuilding.Generator", "ResidentialBuilding.Generator\ResidentialBuilding.Generator.csproj", "{4C3748F7-BE7B-4C97-A656-D0D467E4BC5D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResidentialBuilding.AppHost", "ResidentialBuilding.AppHost\ResidentialBuilding.AppHost.csproj", "{248C1F9B-F012-4C7C-A458-4E2D0F918A70}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResidentialBuilding.ServiceDefaults", "ResidentialBuilding.ServiceDefaults\ResidentialBuilding.ServiceDefaults.csproj", "{3AEE6EF1-603F-411D-89C2-5CE78EBCAEBA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResidentialBuilding.Gateway", "ResidentialBuilding.Gateway\ResidentialBuilding.Gateway.csproj", "{2CC59865-F962-47FF-9C2F-1CB6F535316B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +23,22 @@ Global
{AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|Any CPU.Build.0 = Release|Any CPU
{4C3748F7-BE7B-4C97-A656-D0D467E4BC5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4C3748F7-BE7B-4C97-A656-D0D467E4BC5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C3748F7-BE7B-4C97-A656-D0D467E4BC5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C3748F7-BE7B-4C97-A656-D0D467E4BC5D}.Release|Any CPU.Build.0 = Release|Any CPU
{248C1F9B-F012-4C7C-A458-4E2D0F918A70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{248C1F9B-F012-4C7C-A458-4E2D0F918A70}.Debug|Any CPU.Build.0 = Debug|Any CPU
{248C1F9B-F012-4C7C-A458-4E2D0F918A70}.Release|Any CPU.ActiveCfg = Release|Any CPU
{248C1F9B-F012-4C7C-A458-4E2D0F918A70}.Release|Any CPU.Build.0 = Release|Any CPU
{3AEE6EF1-603F-411D-89C2-5CE78EBCAEBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3AEE6EF1-603F-411D-89C2-5CE78EBCAEBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3AEE6EF1-603F-411D-89C2-5CE78EBCAEBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3AEE6EF1-603F-411D-89C2-5CE78EBCAEBA}.Release|Any CPU.Build.0 = Release|Any CPU
{2CC59865-F962-47FF-9C2F-1CB6F535316B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CC59865-F962-47FF-9C2F-1CB6F535316B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CC59865-F962-47FF-9C2F-1CB6F535316B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CC59865-F962-47FF-9C2F-1CB6F535316B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
26 changes: 26 additions & 0 deletions ResidentialBuilding.AppHost/AppHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("residential-building-cache")
.WithRedisInsight(containerName: "residential-building-insight");

var gateway = builder.AddProject<Projects.ResidentialBuilding_Gateway>("gateway")
.WithEndpoint("http", endpoint => endpoint.Port = 5300)
.WithExternalHttpEndpoints();

const int generatorPortBase = 5200;
for (var i = 1; i <= 5; ++i)
{
var i1 = i;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

var generator = builder.AddProject<Projects.ResidentialBuilding_Generator>($"generator-{i}")
.WithReference(cache, "residential-building-cache")
.WithEndpoint("http", endpoint => endpoint.Port = generatorPortBase + i1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

У тебя и с i порты нормально назначатся

.WaitFor(cache);

gateway.WaitFor(generator);
}

builder.AddProject<Projects.Client_Wasm>("client")
.WithReference(gateway)
.WaitFor(gateway);

builder.Build().Run();
29 changes: 29 additions & 0 deletions ResidentialBuilding.AppHost/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:17129;http://localhost:15221",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21101",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22255"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15221",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19083",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20274"
}
}
}
}
24 changes: 24 additions & 0 deletions ResidentialBuilding.AppHost/ResidentialBuilding.AppHost.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<Sdk Name="Aspire.AppHost.Sdk" Version="9.5.0" />

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>ed7e1e47-dc98-4419-8424-85412466aa9b</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.5.0" />
<PackageReference Include="Aspire.Hosting.Redis" Version="9.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ResidentialBuilding.Generator\ResidentialBuilding.Generator.csproj" />
<ProjectReference Include="..\Client.Wasm\Client.Wasm.csproj" />
<ProjectReference Include="..\ResidentialBuilding.Gateway\ResidentialBuilding.Gateway.csproj" />
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions ResidentialBuilding.AppHost/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions ResidentialBuilding.AppHost/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
64 changes: 64 additions & 0 deletions ResidentialBuilding.Gateway/LoadBalancer/QueryBasedLoadBalancer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Ocelot.LoadBalancer.Interfaces;
using Ocelot.Responses;
using Ocelot.Values;

namespace ResidentialBuilding.Gateway.LoadBalancer;

/// <summary>
/// Query-based балансировщик нагрузки для Ocelot.
/// Выбирает downstream-сервис на основе query-параметра id запроса.
/// </summary>
/// <param name="logger">Логгер.</param>
/// <param name="services">Асинхронная функция, возвращающая актуальный список доступных downstream-сервисов.</param>
public class QueryBasedLoadBalancer(ILogger<QueryBasedLoadBalancer> logger, Func<Task<List<Service>>> services)
: ILoadBalancer
{
private const string IdQueryParamName = "id";

public string Type => nameof(QueryBasedLoadBalancer);

/// <summary>
/// Метод вызывается Ocelot после завершения запроса к downstream-сервису.
/// В текущей реализации ничего не делает (не требуется для query-based подхода).
/// </summary>
public void Release(ServiceHostAndPort hostAndPort) { }

/// <summary>
/// Основной метод выбора downstream-сервиса на основе query-параметра id текущего запроса.
/// </summary>
/// <param name="httpContext">Контекст HTTP-запроса (используется для доступа к Query string).</param>
/// <returns>
/// OkResponse с выбранным адресом сервиса в зависимости от query-параметра id, либо случайно выбранный адрес
/// сервиса, если query-параметр не задан.
/// </returns>
public async Task<Response<ServiceHostAndPort>> LeaseAsync(HttpContext httpContext)
{
var currentServices = await services.Invoke();
var query = httpContext.Request.Query;

if (!query.TryGetValue(IdQueryParamName, out var idValues) || idValues.Count <= 0)
{
return SelectRandomService(currentServices);
}

var idStr = idValues.First();

if (string.IsNullOrWhiteSpace(idStr) || !int.TryParse(idStr, out var id) || id < 0)
{
return SelectRandomService(currentServices);
}

var index = id % currentServices.Count;
logger.LogInformation("Query based selected index={index}.", index);

return new OkResponse<ServiceHostAndPort>(currentServices[index].HostAndPort);
}

private OkResponse<ServiceHostAndPort> SelectRandomService(List<Service> currentServices)
{
var randomIndex = Random.Shared.Next(currentServices.Count);
logger.LogWarning("Query doesn't contain correct id parameter, index={randomIndex} selected by random.", randomIndex);

return new OkResponse<ServiceHostAndPort>(currentServices[randomIndex].HostAndPort);
}
}
35 changes: 35 additions & 0 deletions ResidentialBuilding.Gateway/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using ResidentialBuilding.Gateway.LoadBalancer;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
options.AddPolicy("AllowLocalDev", policy =>
{
policy
.AllowAnyOrigin()
.WithHeaders("Content-Type")
.WithMethods("GET");
});
});

builder.Configuration
.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true)
.AddOcelot();

builder.Services
.AddOcelot(builder.Configuration)
.AddCustomLoadBalancer((sp, _, discoveryProvider) =>
{
var logger = sp.GetRequiredService<ILogger<QueryBasedLoadBalancer>>();
return new QueryBasedLoadBalancer(logger, discoveryProvider.GetAsync);
});

var app = builder.Build();

app.UseCors("AllowLocalDev");
await app.UseOcelot();

await app.RunAsync();
38 changes: 38 additions & 0 deletions ResidentialBuilding.Gateway/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:22127",
"sslPort": 44310
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5298",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7014;http://localhost:5298",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
17 changes: 17 additions & 0 deletions ResidentialBuilding.Gateway/ResidentialBuilding.Gateway.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Ocelot" Version="24.1.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ResidentialBuilding.ServiceDefaults\ResidentialBuilding.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions ResidentialBuilding.Gateway/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions ResidentialBuilding.Gateway/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
20 changes: 20 additions & 0 deletions ResidentialBuilding.Gateway/ocelot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"Routes": [
{
"DownstreamPathTemplate": "/api/residential-building",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{ "Host": "localhost", "Port": 5201 },
{ "Host": "localhost", "Port": 5202 },
{ "Host": "localhost", "Port": 5203 },
{ "Host": "localhost", "Port": 5204 },
{ "Host": "localhost", "Port": 5205 }
],
"UpstreamPathTemplate": "/residential-building",
"UpstreamHttpMethod": [ "GET" ],
"LoadBalancerOptions": {
"Type": "QueryBasedLoadBalancer"
}
}
]
}
Loading