feat(docker, api, core, dal, domain, workloadBuilder): WorkloadBuillder - формирование кэша по интервалу.

This commit is contained in:
Mikhail Trubnikov
2026-05-19 09:45:38 +10:00
parent e5535857d4
commit 178d136dba
16 changed files with 180 additions and 11 deletions

View File

@@ -137,7 +137,6 @@ namespace PARR.API.Controllers.V1.Statistics
var report = await workloadService.GetWorkloadReport(reportType, dateStart, durationDays, offset, filter); var report = await workloadService.GetWorkloadReport(reportType, dateStart, durationDays, offset, filter);
if (report == null) if (report == null)
//return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Необходимо сформировать отчетность." } }));
return BadRequest(new Response<string?>(null, false, new List<ErrorModel> { new ErrorModel { Message = "Необходимо сформировать отчетность." } }, "empty")); return BadRequest(new Response<string?>(null, false, new List<ErrorModel> { new ErrorModel { Message = "Необходимо сформировать отчетность." } }, "empty"));
var resonse = mapper.Map<StatWorkloadReportResponse>(report); var resonse = mapper.Map<StatWorkloadReportResponse>(report);

View File

@@ -0,0 +1,9 @@
using Microsoft.AspNetCore.Diagnostics;
namespace PARR.API.Infrastructure.Middleware
{
public class GlobalExceptionHandler//: IExceptionHandler
{
//todo:
}
}

View File

@@ -111,7 +111,7 @@
} }
], ],
"RabbitMq": { "RabbitMq": {
"ThresholdConnections": 28 "ThresholdConnections": 29
} }
} }
} }

View File

@@ -84,6 +84,8 @@ namespace PARR.Core
services.AddScoped<WorkloadCacheService>(); services.AddScoped<WorkloadCacheService>();
services.AddScoped<IWorkloadService, WorkloadService>(); services.AddScoped<IWorkloadService, WorkloadService>();
services.AddSingleton<IWorkloadIntervalService, WorkloadIntervalService>();
#endregion #endregion
#region NextRun #region NextRun

View File

@@ -11,7 +11,8 @@ namespace PARR.Core.Services.TaskServices.Interfaces
public interface ITaskManagementService public interface ITaskManagementService
{ {
/// <summary> /// <summary>
/// Создать новую задачу в БД и опубликовать в очередь /// Создать новую задачу в БД и опубликовать в очередь.
/// В случае ошибок генерирует Exception.
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="typeCode">Тип задачи</param> /// <param name="typeCode">Тип задачи</param>

View File

@@ -0,0 +1,55 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using PARR.Core.Common.Interfaces;
using PARR.Core.Services.TaskServices.Interfaces;
using PARR.Core.Services.Workload.Interfaces;
using PARR.Domain.Entities.Base.History;
using PARR.Domain.Settings;
namespace PARR.Core.Services.Workload.Implementations
{
internal class WorkloadIntervalService : IWorkloadIntervalService
{
private readonly ILogger<WorkloadIntervalService> logger;
private readonly IIntervalService intervalService;
private readonly IServiceProvider serviceProvider;
public WorkloadIntervalService(
ILogger<WorkloadIntervalService> logger,
IIntervalService intervalService,
IServiceProvider serviceProvider
)
{
this.logger = logger;
this.intervalService = intervalService;
this.serviceProvider = serviceProvider;
}
public async Task StartAsync(TimeSpan interval, IMqSettings mqSettings)
{
logger.LogInformation("Запуск сервиса формирования кэш отчетов Workload по интервалу.");
await intervalService.IntervalInitAsync(async () =>
{
await using (var scope = serviceProvider.CreateAsyncScope())
{
var taskManagementService = scope.ServiceProvider.GetRequiredService<ITaskManagementService>();
var initiator = new HistoryInitiator { InitiatorParrComponentId = Domain.Enums.ParrComponentsEnum.WorkloadBuilder, InitiatorComment = $"Генерация кэш workload по интервалу '{interval}'" };
try
{
var taskId = await taskManagementService.CreateTaskAsync<object?>(Domain.Enums.TaskTypeEnum.Workload, default, initiator, mqSettings);
logger.LogInformation("Создана задача {taskId} на формирование кэш отчета.", taskId);
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при отправке задания на формирование кэш отчета.");
}
}
}, interval);
}
}
}

View File

@@ -0,0 +1,18 @@
using PARR.Domain.Settings;
namespace PARR.Core.Services.Workload.Interfaces
{
/// <summary>
/// Формирование кэш отчетов по интервалу
/// </summary>
public interface IWorkloadIntervalService
{
/// <summary>
/// Запуск формирования по интервалу
/// </summary>
/// <param name="interval">Интервал</param>
/// <param name="mqSettings">Настройки очереди куда отправлять задание</param>
/// <returns></returns>
Task StartAsync(TimeSpan interval, IMqSettings mqSettings);
}
}

View File

@@ -518,7 +518,8 @@ namespace PARR.DAL.Context
new() { Id = ParrComponentsEnum.NextRun, Name = ParrComponentsEnum.NextRun.ToString(), Description = "Рассчет даты следующего срабатывания" }, new() { Id = ParrComponentsEnum.NextRun, Name = ParrComponentsEnum.NextRun.ToString(), Description = "Рассчет даты следующего срабатывания" },
new() { Id = ParrComponentsEnum.TemplateActivator, Name = ParrComponentsEnum.TemplateActivator.ToString(), Description = "Активатор шаблонов / расписаний" }, new() { Id = ParrComponentsEnum.TemplateActivator, Name = ParrComponentsEnum.TemplateActivator.ToString(), Description = "Активатор шаблонов / расписаний" },
new() { Id = ParrComponentsEnum.TemplateDistributor, Name = ParrComponentsEnum.TemplateDistributor.ToString(), Description = "Автораспределение РР" }, new() { Id = ParrComponentsEnum.TemplateDistributor, Name = ParrComponentsEnum.TemplateDistributor.ToString(), Description = "Автораспределение РР" },
new() { Id = ParrComponentsEnum.TemplateTaskGenerator, Name = ParrComponentsEnum.TemplateTaskGenerator.ToString(), Description = "Генератор шаблонов" } new() { Id = ParrComponentsEnum.TemplateTaskGenerator, Name = ParrComponentsEnum.TemplateTaskGenerator.ToString(), Description = "Генератор шаблонов" },
new() { Id = ParrComponentsEnum.WorkloadBuilder, Name = ParrComponentsEnum.WorkloadBuilder.ToString(), Description = "Генератор кэша отчетов workload" }
); );
}); });

View File

@@ -79,6 +79,11 @@ namespace PARR.Domain.Enums
/// <summary> /// <summary>
/// Генератор Template /// Генератор Template
/// </summary> /// </summary>
TemplateTaskGenerator = 13 TemplateTaskGenerator = 13,
/// <summary>
/// Генератор кэша отчетов Workload
/// </summary>
WorkloadBuilder = 14
} }
} }

View File

@@ -0,0 +1,25 @@
using PARR.Core.Services.Workload.Interfaces;
using PARR.WorkloadBuilderWorker.Settings;
namespace PARR.WorkloadBuilderWorker
{
public class IntervalWorker : BackgroundService
{
private readonly ILogger<IntervalWorker> logger;
private readonly IWorkloadIntervalService workloadIntervalService;
private readonly WorkerSettings workerSettings;
public IntervalWorker(ILogger<IntervalWorker> logger, IWorkloadIntervalService workloadIntervalService, WorkerSettings workerSettings)
{
this.logger = logger;
this.workloadIntervalService = workloadIntervalService;
this.workerSettings = workerSettings;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
logger.LogInformation("Запуск {workerName}", nameof(IntervalWorker));
await workloadIntervalService.StartAsync(workerSettings.RepeatEvery, workerSettings.MqSettings);
}
}
}

View File

@@ -46,8 +46,25 @@ builder.Configuration.AddDalConfigurations(builder.Services);
builder.Services.AddDallSettings(builder.Configuration); builder.Services.AddDallSettings(builder.Configuration);
#endregion #endregion
// Задается через переменные окружеия
var runRabbit = builder.Configuration.GetValue<bool>("ENABLE_RABBIT", true);
var runInterval = builder.Configuration.GetValue<bool>("ENABLE_INTERVAL", true);
builder.Services.AddHostedService<Worker>(); if (runRabbit)
{
builder.Services.AddHostedService<RabbitWorker>();
}
if (runInterval)
{
builder.Services.AddHostedService<IntervalWorker>();
}
// Настройка краша при ошибке. Если хоть один из воркеров упадет, то падает целиком проект
builder.Services.Configure<HostOptions>(options =>
{
options.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.StopHost;
});
var host = builder.Build(); var host = builder.Build();
host.Run(); host.Run();

View File

@@ -3,13 +3,15 @@ using PARR.WorkloadBuilderWorker.Settings;
namespace PARR.WorkloadBuilderWorker namespace PARR.WorkloadBuilderWorker
{ {
public class Worker : BackgroundService public class RabbitWorker : BackgroundService
{ {
private readonly ILogger<RabbitWorker> logger;
private readonly IWorkloadCacheBuilderService workloadCacheBuilderService; private readonly IWorkloadCacheBuilderService workloadCacheBuilderService;
private readonly WorkerSettings workerSettings; private readonly WorkerSettings workerSettings;
public Worker(ILogger<Worker> logger, IWorkloadCacheBuilderService workloadCacheBuilderService, WorkerSettings WorkerSettings) public RabbitWorker(ILogger<RabbitWorker> logger, IWorkloadCacheBuilderService workloadCacheBuilderService, WorkerSettings WorkerSettings)
{ {
this.logger = logger;
this.workloadCacheBuilderService = workloadCacheBuilderService; this.workloadCacheBuilderService = workloadCacheBuilderService;
this.workerSettings = WorkerSettings; this.workerSettings = WorkerSettings;
} }
@@ -17,6 +19,7 @@ namespace PARR.WorkloadBuilderWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
logger.LogInformation("Запуск {workerName}", nameof(RabbitWorker));
await workloadCacheBuilderService.ProcessMessagesAsync(workerSettings.MqSettings); await workloadCacheBuilderService.ProcessMessagesAsync(workerSettings.MqSettings);
} }

View File

@@ -4,6 +4,14 @@ namespace PARR.WorkloadBuilderWorker.Settings
{ {
public record WorkerSettings public record WorkerSettings
{ {
/// <summary>
/// Настройки MQ
/// </summary>
public MqSettingsBase MqSettings { get; init; } = new(); public MqSettingsBase MqSettings { get; init; } = new();
/// <summary>
/// Интервал повторений
/// </summary>
public TimeSpan RepeatEvery { get; init; }
} }
} }

View File

@@ -28,6 +28,7 @@
"QueueName": "parr-task-workload", "QueueName": "parr-task-workload",
"User": "task_workload_reader", "User": "task_workload_reader",
"Password": "sdjfgIYFdifyKJFDhvsad@!314d" "Password": "sdjfgIYFdifyKJFDhvsad@!314d"
} },
"RepeatEvery": "12:00:00"
} }
} }

View File

@@ -9,6 +9,7 @@
<DockerServiceName>parr.api</DockerServiceName> <DockerServiceName>parr.api</DockerServiceName>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="docker-compose.task-reconciliation.yml" />
<None Include="docker-compose.template-matcher.yml" /> <None Include="docker-compose.template-matcher.yml" />
<None Include="docker-compose.template-generator.yml" /> <None Include="docker-compose.template-generator.yml" />
<None Include="docker-compose.aihit-loader.yml" /> <None Include="docker-compose.aihit-loader.yml" />
@@ -25,6 +26,7 @@
<None Include="docker-compose.template-activator.yml" /> <None Include="docker-compose.template-activator.yml" />
<None Include="docker-compose.template-distributor.yml" /> <None Include="docker-compose.template-distributor.yml" />
<None Include="docker-compose.template-updater.yml" /> <None Include="docker-compose.template-updater.yml" />
<None Include="docker-compose.workload-builder.yml" />
<None Include="docker-compose.yml" /> <None Include="docker-compose.yml" />
<None Include=".dockerignore" /> <None Include=".dockerignore" />
<None Include="stack.yml" /> <None Include="stack.yml" />

View File

@@ -2,11 +2,13 @@ version: '3.4'
# workload builder # workload builder
services: services:
parr-workload-builder: parr-workload-builder-interval:
image: harbor.dvgd.rzd/parr/parr-workload-builder:${tag:-latest} image: harbor.dvgd.rzd/parr/parr-workload-builder:${tag:-latest}
environment: environment:
- ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_ENVIRONMENT=Production
- TZ=Europe/Moscow - TZ=Europe/Moscow
- ENABLE_RABBIT=false
- ENABLE_INTERVAL=true
logging: logging:
driver: fluentd driver: fluentd
options: options:
@@ -15,7 +17,28 @@ services:
fluentd-max-retries: '30' fluentd-max-retries: '30'
fluentd-async: 'true' fluentd-async: 'true'
fluentd-buffer-limit: '52428800' fluentd-buffer-limit: '52428800'
tag: parr.workload-builder.serilog tag: parr.workload-builder-interval.serilog
deploy:
replicas: 1
networks:
- parr-network
parr-workload-builder-rabbit:
image: harbor.dvgd.rzd/parr/parr-workload-builder:${tag:-latest}
environment:
- ASPNETCORE_ENVIRONMENT=Production
- TZ=Europe/Moscow
- ENABLE_RABBIT=true
- ENABLE_INTERVAL=false
logging:
driver: fluentd
options:
fluentd-address: dvgd-efk-01.dvgd.oao.rzd:24224
fluentd-retry-wait: '10s'
fluentd-max-retries: '30'
fluentd-async: 'true'
fluentd-buffer-limit: '52428800'
tag: parr.workload-builder-rabbit.serilog
deploy: deploy:
replicas: 1 replicas: 1
networks: networks: