diff --git a/PARR.API/Controllers/V1/Statistics/StatWorkloadController.cs b/PARR.API/Controllers/V1/Statistics/StatWorkloadController.cs index f57ad973..ef330478 100644 --- a/PARR.API/Controllers/V1/Statistics/StatWorkloadController.cs +++ b/PARR.API/Controllers/V1/Statistics/StatWorkloadController.cs @@ -137,7 +137,6 @@ namespace PARR.API.Controllers.V1.Statistics var report = await workloadService.GetWorkloadReport(reportType, dateStart, durationDays, offset, filter); if (report == null) - //return BadRequest(new Response(false, new List { new ErrorModel { Message = "Необходимо сформировать отчетность." } })); return BadRequest(new Response(null, false, new List { new ErrorModel { Message = "Необходимо сформировать отчетность." } }, "empty")); var resonse = mapper.Map(report); diff --git a/PARR.API/Infrastructure/Middleware/GlobalExceptionHandler.cs b/PARR.API/Infrastructure/Middleware/GlobalExceptionHandler.cs new file mode 100644 index 00000000..50009b57 --- /dev/null +++ b/PARR.API/Infrastructure/Middleware/GlobalExceptionHandler.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Diagnostics; + +namespace PARR.API.Infrastructure.Middleware +{ + public class GlobalExceptionHandler//: IExceptionHandler + { + //todo: + } +} diff --git a/PARR.API/appsettings.json b/PARR.API/appsettings.json index 1ce93432..01a1a2c6 100644 --- a/PARR.API/appsettings.json +++ b/PARR.API/appsettings.json @@ -111,7 +111,7 @@ } ], "RabbitMq": { - "ThresholdConnections": 28 + "ThresholdConnections": 29 } } } diff --git a/PARR.Core/DependencyInjection.cs b/PARR.Core/DependencyInjection.cs index 89a31080..74f867ac 100644 --- a/PARR.Core/DependencyInjection.cs +++ b/PARR.Core/DependencyInjection.cs @@ -84,6 +84,8 @@ namespace PARR.Core services.AddScoped(); services.AddScoped(); + services.AddSingleton(); + #endregion #region NextRun diff --git a/PARR.Core/Services/TaskServices/Interfaces/ITaskManagementService.cs b/PARR.Core/Services/TaskServices/Interfaces/ITaskManagementService.cs index 24370534..b9b83851 100644 --- a/PARR.Core/Services/TaskServices/Interfaces/ITaskManagementService.cs +++ b/PARR.Core/Services/TaskServices/Interfaces/ITaskManagementService.cs @@ -11,7 +11,8 @@ namespace PARR.Core.Services.TaskServices.Interfaces public interface ITaskManagementService { /// - /// Создать новую задачу в БД и опубликовать в очередь + /// Создать новую задачу в БД и опубликовать в очередь. + /// В случае ошибок генерирует Exception. /// /// /// Тип задачи diff --git a/PARR.Core/Services/Workload/Implementations/WorkloadIntervalService.cs b/PARR.Core/Services/Workload/Implementations/WorkloadIntervalService.cs new file mode 100644 index 00000000..9bc6126d --- /dev/null +++ b/PARR.Core/Services/Workload/Implementations/WorkloadIntervalService.cs @@ -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 logger; + private readonly IIntervalService intervalService; + private readonly IServiceProvider serviceProvider; + + public WorkloadIntervalService( + ILogger 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(); + + var initiator = new HistoryInitiator { InitiatorParrComponentId = Domain.Enums.ParrComponentsEnum.WorkloadBuilder, InitiatorComment = $"Генерация кэш workload по интервалу '{interval}'" }; + + try + { + var taskId = await taskManagementService.CreateTaskAsync(Domain.Enums.TaskTypeEnum.Workload, default, initiator, mqSettings); + logger.LogInformation("Создана задача {taskId} на формирование кэш отчета.", taskId); + } + catch (Exception ex) + { + logger.LogError(ex, "Ошибка при отправке задания на формирование кэш отчета."); + } + } + + }, interval); + } + } +} diff --git a/PARR.Core/Services/Workload/Interfaces/IWorkloadIntervalService.cs b/PARR.Core/Services/Workload/Interfaces/IWorkloadIntervalService.cs new file mode 100644 index 00000000..facaabae --- /dev/null +++ b/PARR.Core/Services/Workload/Interfaces/IWorkloadIntervalService.cs @@ -0,0 +1,18 @@ +using PARR.Domain.Settings; + +namespace PARR.Core.Services.Workload.Interfaces +{ + /// + /// Формирование кэш отчетов по интервалу + /// + public interface IWorkloadIntervalService + { + /// + /// Запуск формирования по интервалу + /// + /// Интервал + /// Настройки очереди куда отправлять задание + /// + Task StartAsync(TimeSpan interval, IMqSettings mqSettings); + } +} diff --git a/PARR.DAL/Context/DataContext.cs b/PARR.DAL/Context/DataContext.cs index 36b68b74..84749f65 100644 --- a/PARR.DAL/Context/DataContext.cs +++ b/PARR.DAL/Context/DataContext.cs @@ -518,7 +518,8 @@ namespace PARR.DAL.Context new() { Id = ParrComponentsEnum.NextRun, Name = ParrComponentsEnum.NextRun.ToString(), Description = "Рассчет даты следующего срабатывания" }, new() { Id = ParrComponentsEnum.TemplateActivator, Name = ParrComponentsEnum.TemplateActivator.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" } ); }); diff --git a/PARR.Domain/Enums/ParrComponentsEnum.cs b/PARR.Domain/Enums/ParrComponentsEnum.cs index 725b1688..d829acb4 100644 --- a/PARR.Domain/Enums/ParrComponentsEnum.cs +++ b/PARR.Domain/Enums/ParrComponentsEnum.cs @@ -79,6 +79,11 @@ namespace PARR.Domain.Enums /// /// Генератор Template /// - TemplateTaskGenerator = 13 + TemplateTaskGenerator = 13, + + /// + /// Генератор кэша отчетов Workload + /// + WorkloadBuilder = 14 } } diff --git a/PARR.WorkloadBuilderWorker/IntervalWorker.cs b/PARR.WorkloadBuilderWorker/IntervalWorker.cs new file mode 100644 index 00000000..4107e248 --- /dev/null +++ b/PARR.WorkloadBuilderWorker/IntervalWorker.cs @@ -0,0 +1,25 @@ +using PARR.Core.Services.Workload.Interfaces; +using PARR.WorkloadBuilderWorker.Settings; + +namespace PARR.WorkloadBuilderWorker +{ + public class IntervalWorker : BackgroundService + { + private readonly ILogger logger; + private readonly IWorkloadIntervalService workloadIntervalService; + private readonly WorkerSettings workerSettings; + + public IntervalWorker(ILogger 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); + } + } +} diff --git a/PARR.WorkloadBuilderWorker/Program.cs b/PARR.WorkloadBuilderWorker/Program.cs index 3c9414ff..30e9c2da 100644 --- a/PARR.WorkloadBuilderWorker/Program.cs +++ b/PARR.WorkloadBuilderWorker/Program.cs @@ -46,8 +46,25 @@ builder.Configuration.AddDalConfigurations(builder.Services); builder.Services.AddDallSettings(builder.Configuration); #endregion +// Задается через переменные окружеия +var runRabbit = builder.Configuration.GetValue("ENABLE_RABBIT", true); +var runInterval = builder.Configuration.GetValue("ENABLE_INTERVAL", true); -builder.Services.AddHostedService(); +if (runRabbit) +{ + builder.Services.AddHostedService(); +} + +if (runInterval) +{ + builder.Services.AddHostedService(); +} + +// Настройка краша при ошибке. Если хоть один из воркеров упадет, то падает целиком проект +builder.Services.Configure(options => +{ + options.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.StopHost; +}); var host = builder.Build(); host.Run(); diff --git a/PARR.WorkloadBuilderWorker/Worker.cs b/PARR.WorkloadBuilderWorker/RabbitWorker.cs similarity index 69% rename from PARR.WorkloadBuilderWorker/Worker.cs rename to PARR.WorkloadBuilderWorker/RabbitWorker.cs index 730de234..d6bca1f5 100644 --- a/PARR.WorkloadBuilderWorker/Worker.cs +++ b/PARR.WorkloadBuilderWorker/RabbitWorker.cs @@ -3,13 +3,15 @@ using PARR.WorkloadBuilderWorker.Settings; namespace PARR.WorkloadBuilderWorker { - public class Worker : BackgroundService + public class RabbitWorker : BackgroundService { + private readonly ILogger logger; private readonly IWorkloadCacheBuilderService workloadCacheBuilderService; private readonly WorkerSettings workerSettings; - public Worker(ILogger logger, IWorkloadCacheBuilderService workloadCacheBuilderService, WorkerSettings WorkerSettings) + public RabbitWorker(ILogger logger, IWorkloadCacheBuilderService workloadCacheBuilderService, WorkerSettings WorkerSettings) { + this.logger = logger; this.workloadCacheBuilderService = workloadCacheBuilderService; this.workerSettings = WorkerSettings; } @@ -17,6 +19,7 @@ namespace PARR.WorkloadBuilderWorker protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + logger.LogInformation("Запуск {workerName}", nameof(RabbitWorker)); await workloadCacheBuilderService.ProcessMessagesAsync(workerSettings.MqSettings); } diff --git a/PARR.WorkloadBuilderWorker/Settings/WorkerSettings.cs b/PARR.WorkloadBuilderWorker/Settings/WorkerSettings.cs index 5ab5e0b0..c55c5cdf 100644 --- a/PARR.WorkloadBuilderWorker/Settings/WorkerSettings.cs +++ b/PARR.WorkloadBuilderWorker/Settings/WorkerSettings.cs @@ -4,6 +4,14 @@ namespace PARR.WorkloadBuilderWorker.Settings { public record WorkerSettings { + /// + /// Настройки MQ + /// public MqSettingsBase MqSettings { get; init; } = new(); + + /// + /// Интервал повторений + /// + public TimeSpan RepeatEvery { get; init; } } } diff --git a/PARR.WorkloadBuilderWorker/appsettings.json b/PARR.WorkloadBuilderWorker/appsettings.json index 496c9590..b1debb85 100644 --- a/PARR.WorkloadBuilderWorker/appsettings.json +++ b/PARR.WorkloadBuilderWorker/appsettings.json @@ -28,6 +28,7 @@ "QueueName": "parr-task-workload", "User": "task_workload_reader", "Password": "sdjfgIYFdifyKJFDhvsad@!314d" - } + }, + "RepeatEvery": "12:00:00" } } diff --git a/docker-compose.dcproj b/docker-compose.dcproj index 825b0fcb..a150e6c7 100644 --- a/docker-compose.dcproj +++ b/docker-compose.dcproj @@ -9,6 +9,7 @@ parr.api + @@ -25,6 +26,7 @@ + diff --git a/docker-compose.workload-builder.yml b/docker-compose.workload-builder.yml index 6994cb4b..7c0805c8 100644 --- a/docker-compose.workload-builder.yml +++ b/docker-compose.workload-builder.yml @@ -2,11 +2,13 @@ version: '3.4' # workload builder services: - parr-workload-builder: + parr-workload-builder-interval: image: harbor.dvgd.rzd/parr/parr-workload-builder:${tag:-latest} environment: - ASPNETCORE_ENVIRONMENT=Production - TZ=Europe/Moscow + - ENABLE_RABBIT=false + - ENABLE_INTERVAL=true logging: driver: fluentd options: @@ -15,7 +17,28 @@ services: fluentd-max-retries: '30' fluentd-async: 'true' 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: replicas: 1 networks: