feat(bll, all): обновлена библиотека RabbitMq.Client до версии 7.1.2. Обновлен MqServiceV2. В IMqSettings добавлен параметр PrefetchCount - макс кол-во одновременных обработок очередей. Обновлены связанные проекты под новый MqService. В проектах Aihit, SyncTemplate, SyncSchedule установлен параметр в конфиг PrefetchCount =100

This commit is contained in:
Mikhail Trubnikov
2025-08-20 14:10:22 +10:00
parent cdd11cdb4d
commit 85798c0956
58 changed files with 621 additions and 341 deletions

View File

@@ -64,7 +64,7 @@ namespace PARR.AIHITLoader
for (var i = 0; i < parts; i++) for (var i = 0; i < parts; i++)
{ {
var batch = preparedData.Skip(i * loaderSettings.PackageSize).Take(loaderSettings.PackageSize).ToList(); var batch = preparedData.Skip(i * loaderSettings.PackageSize).Take(loaderSettings.PackageSize).ToList();
var sendResult = mqService.Send(mqSettings, batch.ToArray()); var sendResult = await mqService.SendAsync(mqSettings, batch.ToArray());
if (sendResult.IsSuccess) if (sendResult.IsSuccess)
{ {
logger.LogInformation($"Данные переданы в RabbitMQ: {batch.Count()}"); logger.LogInformation($"Данные переданы в RabbitMQ: {batch.Count()}");
@@ -75,7 +75,7 @@ namespace PARR.AIHITLoader
} }
} }
await Task.CompletedTask; // await Task.CompletedTask;
} }
/// <summary> /// <summary>

View File

@@ -8,5 +8,6 @@ namespace PARR.AIHITLoader.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -77,7 +77,7 @@ namespace PARR.AIHITMainLoader
for (var i = 0; i < parts; i++) for (var i = 0; i < parts; i++)
{ {
var batch = preparedData.Skip(i * loaderSettings.PackageSize).Take(loaderSettings.PackageSize).ToList(); var batch = preparedData.Skip(i * loaderSettings.PackageSize).Take(loaderSettings.PackageSize).ToList();
var sendResult = mqService.Send(mqSettings, batch.ToArray()); var sendResult = await mqService.SendAsync(mqSettings, batch.ToArray());
if (sendResult.IsSuccess) if (sendResult.IsSuccess)
{ {
logger.LogInformation($"Данные переданы в RabbitMQ: {batch.Count()}"); logger.LogInformation($"Данные переданы в RabbitMQ: {batch.Count()}");
@@ -87,7 +87,7 @@ namespace PARR.AIHITMainLoader
logger.LogError($"Ошибка при передаче данных в Rabbit, не переданные сообщения: {string.Join(", ", sendResult.NotSendMessages!)}"); logger.LogError($"Ошибка при передаче данных в Rabbit, не переданные сообщения: {string.Join(", ", sendResult.NotSendMessages!)}");
} }
await Task.CompletedTask; // await Task.CompletedTask;
} }

View File

@@ -11,5 +11,7 @@ namespace PARR.AIHITMainLoader.Settings
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -25,11 +25,11 @@ namespace PARR.AIHITMainSyncer
this.mqSettings = mqSettings; this.mqSettings = mqSettings;
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
} }
public void Start() public async Task StartAsync()
{ {
logger.LogInformation("Запуск сервиса синхронизации данных из АИХИТ и ПАРР."); logger.LogInformation("Запуск сервиса синхронизации данных из АИХИТ и ПАРР.");
var isConnected = mqService.InitConsumer(mqSettings, async (string msg) => var isConnected = await mqService.InitConsumerAsync(mqSettings, async (string msg) =>
{ {
using (var scope = serviceProvider.CreateScope()) using (var scope = serviceProvider.CreateScope())
{ {
@@ -47,9 +47,9 @@ namespace PARR.AIHITMainSyncer
} }
public void Stop() public async Task StopAsync()
{ {
mqService.Dispose(); await mqService.DisposeAsync();
} }
} }
} }

View File

@@ -2,9 +2,8 @@
{ {
public interface IAihitMainSyncer public interface IAihitMainSyncer
{ {
void Start(); Task StartAsync();
Task StopAsync();
void Stop();
} }
} }

View File

@@ -11,5 +11,7 @@ namespace PARR.AIHITMainSyncer.Settings
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using PARR.AIHITSyncer.Services; using PARR.AIHITSyncer.Services;
using PARR.AIHITSyncer.Settings; using PARR.AIHITSyncer.Settings;
using PARR.BLL.Services.Interfaces; using PARR.BLL.Services.Interfaces;
using System.Threading.Tasks;
namespace PARR.AIHITSyncer namespace PARR.AIHITSyncer
{ {
@@ -27,12 +28,12 @@ namespace PARR.AIHITSyncer
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
} }
public void Start() public async Task StartAsync()
{ {
logger.LogInformation("Запуск сервиса синхронизации данных из АИХИТ и ПАРР."); logger.LogInformation("Запуск сервиса синхронизации данных из АИХИТ и ПАРР.");
//var isConnected = mqService.InitConsumer(mqSettings, SyncDataAsync); //var isConnected = mqService.InitConsumer(mqSettings, SyncDataAsync);
var isConnected = mqService.InitConsumer(mqSettings, async (string msg) => var isConnected = await mqService.InitConsumerAsync(mqSettings, async (string msg) =>
{ {
using (var scope = serviceProvider.CreateScope()) using (var scope = serviceProvider.CreateScope())
{ {
@@ -48,9 +49,9 @@ namespace PARR.AIHITSyncer
if (!isConnected) if (!isConnected)
throw new Exception("Ошибка при подключении к RabbitMq"); throw new Exception("Ошибка при подключении к RabbitMq");
} }
public void Stop() public async Task StopAsync()
{ {
mqService.Dispose(); await mqService.DisposeAsync();
} }
} }

View File

@@ -2,7 +2,7 @@
{ {
public interface IAihitSyncer public interface IAihitSyncer
{ {
void Start(); Task StartAsync();
void Stop(); Task StopAsync();
} }
} }

View File

@@ -8,5 +8,6 @@ namespace PARR.AIHITSyncer.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -16,13 +16,13 @@ namespace PARR.AIHITSyncerWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
aihitSyncer.Start(); await aihitSyncer.StartAsync();
} }
public override Task StopAsync(CancellationToken cancellationToken) public override Task StopAsync(CancellationToken cancellationToken)
{ {
aihitSyncer.Stop(); aihitSyncer.StopAsync().Wait();
return base.StopAsync(cancellationToken); return base.StopAsync(cancellationToken);
} }

View File

@@ -33,6 +33,7 @@
"HostName": "10.99.253.216", "HostName": "10.99.253.216",
"QueueName": "parr-aihit-data", "QueueName": "parr-aihit-data",
"User": "aihit_syncer", "User": "aihit_syncer",
"Password": "afsdgDGjhfiU76496as42!@" "Password": "afsdgDGjhfiU76496as42!@",
"PrefetchCount": 100
} }
} }

View File

@@ -53,7 +53,7 @@ namespace PARR.API.Controllers.V1
var msg = JsonSerializer.Serialize(requestToMq); var msg = JsonSerializer.Serialize(requestToMq);
var sendResult = mqService.Send(mqSettings.TemplateDistributor, new[] { msg }); var sendResult = await mqService.SendAsync(mqSettings.TemplateDistributor, new[] { msg });
if (sendResult.IsSuccess) if (sendResult.IsSuccess)
return Created("", new Response<string?>(null, true, new List<ErrorModel>(), "Отправлен запрос на перераспределение регламентных работ.")); return Created("", new Response<string?>(null, true, new List<ErrorModel>(), "Отправлен запрос на перераспределение регламентных работ."));

View File

@@ -54,7 +54,8 @@ namespace PARR.API.Controllers.V1
request.Ek.ForEach(ek => request.Ek.ForEach(ek =>
{ {
request.Status.ForEach(status => //todo: проверить в ForEach(async status - отрабатывает ли нормально async, возможно надо переделать в обычный foreach
request.Status.ForEach(async status =>
{ {
var obj = new GeneratorTemplateMq var obj = new GeneratorTemplateMq
{ {
@@ -70,7 +71,7 @@ namespace PARR.API.Controllers.V1
var msg = JsonSerializer.Serialize(obj); var msg = JsonSerializer.Serialize(obj);
var _result = mqService.Send(mqSettings.GenerateTemplates, new[] { msg }); var _result = await mqService.SendAsync(mqSettings.GenerateTemplates, new[] { msg });
if (_result.IsSuccess == false) if (_result.IsSuccess == false)
result = false; result = false;

View File

@@ -74,7 +74,7 @@ namespace PARR.API.Controllers.V1
var msg = JsonSerializer.Serialize(mqRequest); var msg = JsonSerializer.Serialize(mqRequest);
var sendResult = mqService.Send(mqSettings.TemplateActivator, new[] { msg }); var sendResult = await mqService.SendAsync(mqSettings.TemplateActivator, new[] { msg });
if (!sendResult.IsSuccess) if (!sendResult.IsSuccess)
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при отправке данных." } })); return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при отправке данных." } }));

View File

@@ -26,7 +26,6 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="RabbitMQ.Client" Version="6.5.0" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" /> <PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" /> <PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />

View File

@@ -16,6 +16,7 @@ namespace PARR.API.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
public class MqTemplateDistributor : IMqSettings public class MqTemplateDistributor : IMqSettings
@@ -24,6 +25,7 @@ namespace PARR.API.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
public class MqTemplateActivator : IMqSettings public class MqTemplateActivator : IMqSettings
@@ -32,6 +34,7 @@ namespace PARR.API.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
public class MqStatistics public class MqStatistics
@@ -45,5 +48,6 @@ namespace PARR.API.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -9,5 +9,9 @@
string QueueName { get; set; } string QueueName { get; set; }
string User { get; set; } string User { get; set; }
string Password { get; set; } string Password { get; set; }
/// <summary>
/// Кол-во сообщений которые может принимать consumer за один раз. 0 или null - без ограничений
/// </summary>
ushort? PrefetchCount { get; set; }
} }
} }

View File

@@ -12,7 +12,7 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="RabbitMQ.Client" Version="6.5.0" /> <PackageReference Include="RabbitMQ.Client" Version="7.1.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -17,7 +17,10 @@ namespace PARR.BLL
services.AddTransient<IFileService, FileService>(); services.AddTransient<IFileService, FileService>();
services.AddTransient<IMqService, MqService>();
//services.AddTransient<IMqService, MqService>();
services.AddTransient<IMqService, MqServiceV2>();
services.AddHttpClient<IMqAdminService, MqAdminService>(); services.AddHttpClient<IMqAdminService, MqAdminService>();
services.AddTransient<ITransformService, TransformService>(); services.AddTransient<ITransformService, TransformService>();
services.AddTransient<IIntervalService, IntervalService>(); services.AddTransient<IIntervalService, IntervalService>();

View File

@@ -4,18 +4,19 @@ using PARR.BLL.Domain.Mq;
using PARR.BLL.Services.Interfaces; using PARR.BLL.Services.Interfaces;
using RabbitMQ.Client; using RabbitMQ.Client;
using RabbitMQ.Client.Events; using RabbitMQ.Client.Events;
using System.Runtime;
using System.Text; using System.Text;
namespace PARR.BLL.Services.Implementations namespace PARR.BLL.Services.Implementations
{ {
internal class MqService : IMqService // RabbitMqClient 6.5.0
internal class MqService// : IMqService
{ {
private readonly ILogger<MqService> logger; private readonly ILogger<MqService> logger;
private IConnection? connection; private IConnection? connection;
private IModel? channel; //private IModel? channel;
public MqService(ILogger<MqService> logger) public MqService(ILogger<MqService> logger)
{ {
@@ -24,144 +25,148 @@ namespace PARR.BLL.Services.Implementations
public MqSendResult Send(IMqSettings mqSettings, string[] msgList) public MqSendResult Send(IMqSettings mqSettings, string[] msgList)
{ {
var factory = new ConnectionFactory return new MqSendResult { IsSuccess = false, NotSendMessages = new string[0] };
{
HostName = mqSettings.HostName,
UserName = mqSettings.User,
Password = mqSettings.Password
};
// индекс текущей отправки в очередь из массива msgList //var factory = new ConnectionFactory
// нужен для формирования списка неотправленных сообщений //{
var sendIdx = 0; // HostName = mqSettings.HostName,
// UserName = mqSettings.User,
// Password = mqSettings.Password
//};
try //// индекс текущей отправки в очередь из массива msgList
{ //// нужен для формирования списка неотправленных сообщений
using (var connection = factory.CreateConnection()) //var sendIdx = 0;
using (var channel = connection.CreateModel())
{
//https://www.rabbitmq.com/lazy-queues.html
//Рекомендовано при работе порциями использовать ленивые очереди. сообщения не используют память
//Формируем соответствующий аргумент
var args = new Dictionary<string, object> { { "x-queue-mode", "lazy" } };
//Объявляем очередь с которой будем работать. //try
//Если такой очереди ещё нет, то создатся. //{
//Если нет, нужно параметры типа durable должны совпадать иначе будет ошибка. // using (var connection = factory.CreateConnection())
//В целом эти параметры можно посмотреть в админке RabbitMQ // using (var channel = connection.CreateModel())
// {
// //https://www.rabbitmq.com/lazy-queues.html
// //Рекомендовано при работе порциями использовать ленивые очереди. сообщения не используют память
// //Формируем соответствующий аргумент
// var args = new Dictionary<string, object> { { "x-queue-mode", "lazy" } };
channel.QueueDeclare( // //Объявляем очередь с которой будем работать.
queue: mqSettings.QueueName, // //Если такой очереди ещё нет, то создатся.
durable: true, // //Если нет, нужно параметры типа durable должны совпадать иначе будет ошибка.
exclusive: false, // //В целом эти параметры можно посмотреть в админке RabbitMQ
autoDelete: false,
arguments: args
);
var props = channel.CreateBasicProperties(); // channel.QueueDeclare(
//храним на диске // queue: mqSettings.QueueName,
props.DeliveryMode = 2; // durable: true,
//время жизни, мс // exclusive: false,
//props.Expiration = "60000"; // autoDelete: false,
// arguments: args
// );
foreach (var msg in msgList) // var props = channel.CreateBasicProperties();
{ // //храним на диске
var body = Encoding.UTF8.GetBytes(msg); // props.DeliveryMode = 2;
channel.BasicPublish(exchange: "", routingKey: mqSettings.QueueName, basicProperties: props, body: body); // //время жизни, мс
// //props.Expiration = "60000";
sendIdx++; // foreach (var msg in msgList)
logger.LogDebug($"Отправлено сообщение в очередь: {mqSettings.HostName} {mqSettings.QueueName}, {msg}"); // {
} // var body = Encoding.UTF8.GetBytes(msg);
} // channel.BasicPublish(exchange: "", routingKey: mqSettings.QueueName, basicProperties: props, body: body);
return new MqSendResult { IsSuccess = true }; // sendIdx++;
} // logger.LogDebug($"Отправлено сообщение в очередь: {mqSettings.HostName} {mqSettings.QueueName}, {msg}");
catch (Exception ex) // }
{ // }
var notSendMsg = new List<string>();
// формируем список неотправленных элементов // return new MqSendResult { IsSuccess = true };
for (int i = sendIdx; i < msgList.Length; i++) //}
notSendMsg.Add(msgList[i]); //catch (Exception ex)
//{
// var notSendMsg = new List<string>();
logger.LogError(ex, $"Ошибка при отправке сообщения в очередь {mqSettings.HostName} {mqSettings.QueueName}, {string.Join(',', notSendMsg)}"); // // формируем список неотправленных элементов
// for (int i = sendIdx; i < msgList.Length; i++)
// notSendMsg.Add(msgList[i]);
return new MqSendResult { IsSuccess = false, NotSendMessages = notSendMsg.ToArray() }; // logger.LogError(ex, $"Ошибка при отправке сообщения в очередь {mqSettings.HostName} {mqSettings.QueueName}, {string.Join(',', notSendMsg)}");
}
// return new MqSendResult { IsSuccess = false, NotSendMessages = notSendMsg.ToArray() };
//}
} }
public bool InitConsumer(IMqSettings mqSettings, MqMessageHandlerDelegate messageHandler) public bool InitConsumer(IMqSettings mqSettings, MqMessageHandlerDelegate messageHandler)
{ {
logger.LogInformation($"Устанавливаю соединение с RabbitMQ: {mqSettings.HostName}, {mqSettings.QueueName}"); logger.LogInformation($"Устанавливаю соединение с RabbitMQ: {mqSettings.HostName}, {mqSettings.QueueName}");
var facory = new ConnectionFactory return false;
{
HostName = mqSettings.HostName,
UserName = mqSettings.User,
Password = mqSettings.Password,
AutomaticRecoveryEnabled = true,
DispatchConsumersAsync = true
};
try //var facory = new ConnectionFactory
{ //{
//using var connection = facory.CreateConnection(); // HostName = mqSettings.HostName,
//using var channel = connection.CreateModel(); // UserName = mqSettings.User,
// Password = mqSettings.Password,
// AutomaticRecoveryEnabled = true,
// DispatchConsumersAsync = true
//};
connection = facory.CreateConnection(); //try
channel = connection.CreateModel(); //{
// //using var connection = facory.CreateConnection();
// //using var channel = connection.CreateModel();
var args = new Dictionary<string, object> { { "x-queue-mode", "lazy" } }; // connection = facory.CreateConnection();
// channel = connection.CreateModel();
channel.QueueDeclare( // var args = new Dictionary<string, object> { { "x-queue-mode", "lazy" } };
queue: mqSettings.QueueName,
durable: true,
exclusive: false,
autoDelete: false,
arguments: args
);
logger.LogInformation($"Соединение с RabbitMQ установлено: {mqSettings.HostName}, {mqSettings.QueueName}"); // channel.QueueDeclare(
// queue: mqSettings.QueueName,
// durable: true,
// exclusive: false,
// autoDelete: false,
// arguments: args
// );
channel.CallbackException += (ch, ea) => // logger.LogInformation($"Соединение с RabbitMQ установлено: {mqSettings.HostName}, {mqSettings.QueueName}");
{
var _channel = (IModel?)ch;
// может тут если канал упал переоткрывать его
logger.LogError(ea.Exception, $"Ошибка при обработке очереди MqService сообщений из очереди. Канал открыт: {_channel?.IsOpen}");
};
var consumer = new AsyncEventingBasicConsumer(channel); // channel.CallbackException += (ch, ea) =>
consumer.Received += async (ch, ea) => // {
{ // var _channel = (IModel?)ch;
var content = Encoding.UTF8.GetString(ea.Body.ToArray()); // // может тут если канал упал переоткрывать его
logger.LogDebug($"Получено сообщение: {content}"); // logger.LogError(ea.Exception, $"Ошибка при обработке очереди MqService сообщений из очереди. Канал открыт: {_channel?.IsOpen}");
// };
await messageHandler.Invoke(content); // var consumer = new AsyncEventingBasicConsumer(channel);
// consumer.Received += async (ch, ea) =>
// {
// var content = Encoding.UTF8.GetString(ea.Body.ToArray());
// logger.LogDebug($"Получено сообщение: {content}");
channel.BasicAck(ea.DeliveryTag, false); // await messageHandler.Invoke(content);
await Task.Yield();
};
string consumerTag = channel.BasicConsume(mqSettings.QueueName, false, consumer); // channel.BasicAck(ea.DeliveryTag, false);
// await Task.Yield();
// };
return true; // string consumerTag = channel.BasicConsume(mqSettings.QueueName, false, consumer);
} // return true;
catch (Exception ex)
{
logger.LogError(ex, $"Ошибка при получении сообщений из очереди: {mqSettings.HostName}, {mqSettings.QueueName}");
return false; //}
} //catch (Exception ex)
//{
// logger.LogError(ex, $"Ошибка при получении сообщений из очереди: {mqSettings.HostName}, {mqSettings.QueueName}");
// return false;
//}
} }
public void Dispose() public void Dispose()
{ {
channel?.Close(); //channel?.Close();
connection?.Close(); //connection?.Close();
channel?.Dispose(); //channel?.Dispose();
connection?.Dispose(); //connection?.Dispose();
} }
} }
} }

View File

@@ -0,0 +1,203 @@
using Microsoft.Extensions.Logging;
using PARR.BLL.Contracts.Interfaces;
using PARR.BLL.Domain.Mq;
using PARR.BLL.Services.Interfaces;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
namespace PARR.BLL.Services.Implementations
{
internal class MqServiceV2 : IMqService
{
private readonly ILogger<MqServiceV2> logger;
private IConnection? consumerConnection;
private IChannel? consumerChannel;
// реализация для RabbitMQ.Client 7.1.2
public MqServiceV2(ILogger<MqServiceV2> logger)
{
this.logger = logger;
}
public async Task<bool> InitConsumerAsync(IMqSettings mqSettings, MqMessageHandlerDelegate messageHandler)
{
logger.LogInformation($"Устанавливаю соединение с RabbitMQ: {mqSettings.HostName}, {mqSettings.QueueName}");
var factory = new ConnectionFactory
{
HostName = mqSettings.HostName,
UserName = mqSettings.User,
Password = mqSettings.Password,
AutomaticRecoveryEnabled = true
};
try
{
consumerConnection = await factory.CreateConnectionAsync();
consumerChannel = await consumerConnection.CreateChannelAsync();
// кол-во сообщений которые можно обрабатывать за раз
if (mqSettings.PrefetchCount.HasValue)
{
await consumerChannel.BasicQosAsync(0, mqSettings.PrefetchCount.Value, false);
}
// создаем очередь, вдруг ее еще нет
await QueueDeclareAsync(consumerChannel, mqSettings);
//await consumerChannel.QueueBindAsync(mqSettings.QueueName, "default", "routingKey");
consumerChannel.CallbackExceptionAsync += async (ch, ea) =>
{
var _channel = (IChannel?)ch;
// может тут если канал упал переоткрывать его
logger.LogError(ea.Exception, $"Ошибка при обработке очереди MqService сообщений из очереди. Канал открыт?: {_channel?.IsOpen}");
//if (consumerChannel.IsClosed)
//{
// //todo: этот момент проверить, успешно ли он переоткроет канал и очередь будет дальше обрабатываться
// await consumerChannel.CloseAsync();
// consumerChannel = await consumerConnection.CreateChannelAsync();
// logger.LogInformation(ea.Exception, $"Переоткрыл канал RabbitMq.");
//}
};
var consumer = new AsyncEventingBasicConsumer(consumerChannel);
//consumer.HandleChannelShutdownAsync = async (c, ea) =>{ };
consumer.ReceivedAsync += async (ch, ea) =>
{
var content = Encoding.UTF8.GetString(ea.Body.ToArray());
logger.LogDebug($"Получено сообщение: {content}");
await messageHandler.Invoke(content);
await consumerChannel.BasicAckAsync(ea.DeliveryTag, false);
};
var consumerTag = await consumerChannel.BasicConsumeAsync(mqSettings.QueueName, false, consumer);
return true;
}
catch (Exception ex)
{
logger.LogError(ex, $"Ошибка при получении сообщений из очереди: {mqSettings.HostName}, {mqSettings.QueueName}");
return false;
}
}
public async Task<MqSendResult> SendAsync(IMqSettings mqSettings, string[] msgList)
{
var factory = new ConnectionFactory
{
HostName = mqSettings.HostName,
UserName = mqSettings.User,
Password = mqSettings.Password,
AutomaticRecoveryEnabled = true
};
// индекс текущей отправки в очередь из массива msgList
// нужен для формирования списка неотправленных сообщений
var sendIdx = 0;
try
{
using (var connection = await factory.CreateConnectionAsync())
using (var channel = await connection.CreateChannelAsync())
{
// создаем очередь, вдруг ее еще нет
await QueueDeclareAsync(channel, mqSettings);
var props = new BasicProperties();
//храним на диске
props.DeliveryMode = DeliveryModes.Persistent;
//время жизни, мс
props.Expiration = "60000";
props.ContentType = "text/plain";//"application/json";
foreach (var msg in msgList)
{
var body = Encoding.UTF8.GetBytes(msg);
await channel.BasicPublishAsync(
exchange: "",
routingKey: mqSettings.QueueName,
//TODO: проверить mandatory: true, может указать его в false
mandatory: true,
basicProperties: props,
body: body
);
sendIdx++;
logger.LogDebug($"Отправлено сообщение в очередь: {mqSettings.HostName} {mqSettings.QueueName}, {msg}");
}
}
return new MqSendResult { IsSuccess = true };
}
catch (Exception ex)
{
var notSendMsg = new List<string>();
// формируем список неотправленных элементов
for (int i = sendIdx; i < msgList.Length; i++)
notSendMsg.Add(msgList[i]);
logger.LogError(ex, $"Ошибка при отправке сообщения в очередь {mqSettings.HostName} {mqSettings.QueueName}, {string.Join(',', notSendMsg)}");
return new MqSendResult { IsSuccess = false, NotSendMessages = notSendMsg.ToArray() };
}
}
private async Task QueueDeclareAsync(IChannel channel, IMqSettings mqSettings)
{
//https://www.rabbitmq.com/lazy-queues.html
// lazy - ленивой очереди больше нет
var args = new Dictionary<string, object?> { { "x-queue-mode", "lazy" } };
//Объявляем очередь с которой будем работать.
//Если такой очереди ещё нет, то создатся.
//Если нет, нужно параметры типа durable должны совпадать иначе будет ошибка.
//В целом эти параметры можно посмотреть в админке RabbitMQ
await channel.QueueDeclareAsync(
queue: mqSettings.QueueName,
durable: true,
exclusive: false,
autoDelete: false,
arguments: args
);
}
public async ValueTask DisposeAsync()
{
if (consumerChannel != null)
{
if (consumerChannel.IsOpen)
{
await consumerChannel.CloseAsync();
}
await consumerChannel.DisposeAsync();
}
if (consumerConnection != null)
{
if (consumerConnection.IsOpen)
{
await consumerConnection.CloseAsync();
}
await consumerConnection.DisposeAsync();
}
}
}
}

View File

@@ -5,9 +5,9 @@ namespace PARR.BLL.Services.Interfaces
{ {
public delegate Task MqMessageHandlerDelegate(string msg); public delegate Task MqMessageHandlerDelegate(string msg);
public interface IMqService : IDisposable public interface IMqService : IAsyncDisposable // IDisposable
{ {
bool InitConsumer(IMqSettings mqSettings, MqMessageHandlerDelegate messageHandler); Task<bool> InitConsumerAsync(IMqSettings mqSettings, MqMessageHandlerDelegate messageHandler);
MqSendResult Send(IMqSettings mqSettings, string[] msgList); Task<MqSendResult> SendAsync(IMqSettings mqSettings, string[] msgList);
} }
} }

View File

@@ -8,6 +8,7 @@ using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces;
using PARR.EsppOrderManager.Services; using PARR.EsppOrderManager.Services;
using PARR.EsppOrderManager.Settings; using PARR.EsppOrderManager.Settings;
using System.Threading.Tasks;
namespace PARR.EsppOrderManager namespace PARR.EsppOrderManager
{ {
@@ -40,11 +41,11 @@ namespace PARR.EsppOrderManager
this.resultTemplateSettings = resultTemplateSettings; this.resultTemplateSettings = resultTemplateSettings;
} }
public void Start() public async Task StartAsync()
{ {
// логика работы описана в документации // логика работы описана в документации
var isConnected = mqService.InitConsumer(mqSettings, ManageOrderAsync); var isConnected = await mqService.InitConsumerAsync(mqSettings, ManageOrderAsync);
if (!isConnected) if (!isConnected)
throw new Exception("Ошибка при подключении к RabbitMq"); throw new Exception("Ошибка при подключении к RabbitMq");
@@ -53,9 +54,9 @@ namespace PARR.EsppOrderManager
//await ManageOrderAsync("{\"OrderId\":\"49191af5-c313-4388-9720-a61fd13f15a8\"}"); //await ManageOrderAsync("{\"OrderId\":\"49191af5-c313-4388-9720-a61fd13f15a8\"}");
} }
public void Stop() public async Task StopAsync()
{ {
mqService.Dispose(); await mqService.DisposeAsync();
} }

View File

@@ -2,7 +2,7 @@
{ {
public interface IEsppOrderManager public interface IEsppOrderManager
{ {
void Start(); Task StartAsync();
void Stop(); Task StopAsync();
} }
} }

View File

@@ -8,5 +8,6 @@ namespace PARR.EsppOrderManager.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -15,12 +15,12 @@ namespace PARR.EsppOrderManagerWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
esppOrderManager.Start(); await esppOrderManager.StartAsync();
} }
public override Task StopAsync(CancellationToken cancellationToken) public override Task StopAsync(CancellationToken cancellationToken)
{ {
esppOrderManager.Stop(); esppOrderManager.StopAsync().Wait();
return base.StopAsync(cancellationToken); return base.StopAsync(cancellationToken);
} }

View File

@@ -2,7 +2,7 @@
{ {
public interface IScheduleSyncher public interface IScheduleSyncher
{ {
void Start(); Task StartAsync();
void Stop(); Task StopAsync();
} }
} }

View File

@@ -20,7 +20,7 @@ namespace PARR.EsppScheduleSync
private readonly ISyncService<EsppObjectSchedule> syncService; private readonly ISyncService<EsppObjectSchedule> syncService;
private readonly SettingsFromDb settingsFromDb; private readonly SettingsFromDb settingsFromDb;
private readonly IServiceProvider serviceProvider; private readonly IServiceProvider serviceProvider;
private readonly INextRunModifierService nextRunModifierService; //private readonly INextRunModifierService nextRunModifierService;
public ScheduleSyncher( public ScheduleSyncher(
ILogger<ScheduleSyncher> logger, ILogger<ScheduleSyncher> logger,
@@ -28,8 +28,8 @@ namespace PARR.EsppScheduleSync
IMqService mqService, IMqService mqService,
ISyncService<EsppObjectSchedule> syncService, ISyncService<EsppObjectSchedule> syncService,
SettingsFromDb settingsFromDb, SettingsFromDb settingsFromDb,
IServiceProvider serviceProvider, IServiceProvider serviceProvider
INextRunModifierService nextRunModifierService //INextRunModifierService nextRunModifierService
) )
{ {
this.logger = logger; this.logger = logger;
@@ -38,7 +38,7 @@ namespace PARR.EsppScheduleSync
this.syncService = syncService; this.syncService = syncService;
this.settingsFromDb = settingsFromDb; this.settingsFromDb = settingsFromDb;
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
this.nextRunModifierService = nextRunModifierService; //this.nextRunModifierService = nextRunModifierService;
if (globalSettings.MqSettings == null) if (globalSettings.MqSettings == null)
{ {
logger.LogError("Нет секции настроек хранилища. MqSettings, EsppTemplates"); logger.LogError("Нет секции настроек хранилища. MqSettings, EsppTemplates");
@@ -47,9 +47,9 @@ namespace PARR.EsppScheduleSync
} }
public void Start() public async Task StartAsync()
{ {
var isConnected = mqService.InitConsumer(globalSettings!.MqSettings!, SyncScheduleAsync); var isConnected = await mqService.InitConsumerAsync(globalSettings!.MqSettings!, SyncScheduleAsync);
if (!isConnected) if (!isConnected)
throw new Exception("Ошибка при подключении к RabbitMq"); throw new Exception("Ошибка при подключении к RabbitMq");
@@ -58,9 +58,9 @@ namespace PARR.EsppScheduleSync
} }
public void Stop() public async Task StopAsync()
{ {
mqService.Dispose(); await mqService.DisposeAsync();
logger.LogInformation($"=== === === Соединение с очередью {globalSettings.MqSettings!.QueueName} закрыто === === ==="); logger.LogInformation($"=== === === Соединение с очередью {globalSettings.MqSettings!.QueueName} закрыто === === ===");
} }
@@ -80,6 +80,19 @@ namespace PARR.EsppScheduleSync
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
private EsppObjectSchedule ConvertDbObjToEsppObj(Template template) private EsppObjectSchedule ConvertDbObjToEsppObj(Template template)
{ {
//дефолтное значение, изменится в сервисе nextRunModifierService
var nextRunByAccountRobotTimeZone = DateTimeOffset.MinValue;
using (var scope = serviceProvider.CreateScope())
{
var nextRunModifierService = scope.ServiceProvider.GetService<INextRunModifierService>();
if (nextRunModifierService == null)
throw new Exception($"Не найден сервис: {nameof(INextRunModifierService)}");
nextRunByAccountRobotTimeZone = nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun);
}
var esppObjectFromDb = new EsppObjectSchedule var esppObjectFromDb = new EsppObjectSchedule
{ {
TemplateName = template.Name, TemplateName = template.Name,
@@ -92,8 +105,10 @@ namespace PARR.EsppScheduleSync
//Мы решили, что для всех расписаний "Нет исключений", если что-то поменяется, тут нужно переделать //Мы решили, что для всех расписаний "Нет исключений", если что-то поменяется, тут нужно переделать
TypeV60calendar = settingsFromDb.ScheduleExclude == "Нет исключений" ? "NONE" : "", TypeV60calendar = settingsFromDb.ScheduleExclude == "Нет исключений" ? "NONE" : "",
//Scheduled = EsppScheduleHelpers.GetNextRun(template.NextRun), //Scheduled = EsppScheduleHelpers.GetNextRun(template.NextRun),
Scheduled = EsppScheduleHelpers.GetNextRun(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)), //Scheduled = EsppScheduleHelpers.GetNextRun(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)),
BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)), //BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunModifierService.GetNextRunByAccountRobotTimeZone(template.NextRun)),
Scheduled = EsppScheduleHelpers.GetNextRun(nextRunByAccountRobotTimeZone),
BasisTime = EsppScheduleHelpers.GetGenerationTime(nextRunByAccountRobotTimeZone),
Timezone = settingsFromDb.ScheduleTimezone, Timezone = settingsFromDb.ScheduleTimezone,
//Мы решили, что для всех расписаний "Отсутствует дата завершения", если что-то поменяется, тут нужно переделать //Мы решили, что для всех расписаний "Отсутствует дата завершения", если что-то поменяется, тут нужно переделать
TerminationType = settingsFromDb.ScheduleRepeatRange == "Отсутствует дата завершения" ? "forever" : "", TerminationType = settingsFromDb.ScheduleRepeatRange == "Отсутствует дата завершения" ? "forever" : "",
@@ -151,7 +166,7 @@ namespace PARR.EsppScheduleSync
//} //}
//else //else
//{ //{
esppObject.Dayofmonth = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue; esppObject.Dayofmonth = esppSchedule.Values.First(t => t.Order == 0).Value.EsppExportValue;
//} //}
break; break;
case EsppSchTypeScheduleEnum.Monthly2: case EsppSchTypeScheduleEnum.Monthly2:

View File

@@ -14,5 +14,6 @@ namespace PARR.EsppScheduleSync.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -1,4 +1,3 @@
using PARR.DAL.TransformServices;
using PARR.EsppScheduleSync; using PARR.EsppScheduleSync;
namespace PARR.EsppScheduleSyncWorker namespace PARR.EsppScheduleSyncWorker
@@ -16,12 +15,12 @@ namespace PARR.EsppScheduleSyncWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
scheduleSyncher.Start(); await scheduleSyncher.StartAsync();
} }
public override Task StopAsync(CancellationToken cancellationToken) public override Task StopAsync(CancellationToken cancellationToken)
{ {
scheduleSyncher.Stop(); scheduleSyncher.StopAsync().Wait();
return base.StopAsync(cancellationToken); return base.StopAsync(cancellationToken);
} }

View File

@@ -32,7 +32,8 @@
"HostName": "parr-rabbitmq", "HostName": "parr-rabbitmq",
"QueueName": "parr-espp-schedulers", "QueueName": "parr-espp-schedulers",
"User": "espp_schedulers_reader", "User": "espp_schedulers_reader",
"Password": "KjdhGLJDshgd&^%84S2" "Password": "KjdhGLJDshgd&^%84S2",
"PrefetchCount": 100
}, },
"ParsingSeparator": "<|>" "ParsingSeparator": "<|>"
} }

View File

@@ -12,21 +12,25 @@ namespace PARR.EsppSync
internal class SyncService<EsppObject> : ISyncService<EsppObject> where EsppObject : class, IEsppObject internal class SyncService<EsppObject> : ISyncService<EsppObject> where EsppObject : class, IEsppObject
{ {
private readonly ILogger<SyncService<EsppObject>> logger; private readonly ILogger<SyncService<EsppObject>> logger;
private readonly ITemplateService templateService; private readonly IServiceProvider serviceProvider;
private readonly IRobotConfigurationService robotConfigurationService;
private readonly IShortcodesService shortcodesService; //private readonly ITemplateService templateService;
//private readonly IRobotConfigurationService robotConfigurationService;
//private readonly IShortcodesService shortcodesService;
public SyncService( public SyncService(
ILogger<SyncService<EsppObject>> logger, ILogger<SyncService<EsppObject>> logger,
ITemplateService templateService, IServiceProvider serviceProvider
IRobotConfigurationService robotConfigurationService, //ITemplateService templateService,
IShortcodesService shortcodesService //IRobotConfigurationService robotConfigurationService,
//IShortcodesService shortcodesService
) )
{ {
this.logger = logger; this.logger = logger;
this.templateService = templateService; this.serviceProvider = serviceProvider;
this.robotConfigurationService = robotConfigurationService; //this.templateService = templateService;
this.shortcodesService = shortcodesService; //this.robotConfigurationService = robotConfigurationService;
//this.shortcodesService = shortcodesService;
} }
@@ -52,106 +56,114 @@ namespace PARR.EsppSync
return; return;
} }
try using (var scope = serviceProvider.CreateScope())
{ {
var template = await templateService.GetTemplateByNameAsync(esppObject.TemplateName); var templateService = GetServiceInScope<ITemplateService>(scope);
var robotConfigurationService = GetServiceInScope<IRobotConfigurationService>(scope);
var shortcodesService = GetServiceInScope<IShortcodesService>(scope);
//todo: существует в ЕСПП но отсутствует в ПАРР. Может его деактивировать или еще что-то сделать. Пока просто пропустим
if (template == null) try
{ {
logger.LogWarning($"Найден объект в ЕСПП с именем шаблона {esppObject.TemplateName} незарегистрированный в ПАРР."); var template = await templateService.GetTemplateByNameAsync(esppObject.TemplateName);
return; //todo: существует в ЕСПП но отсутствует в ПАРР. Может его деактивировать или еще что-то сделать. Пока просто пропустим
} if (template == null)
else
{
var dbObjectInEsppObject = converterToEsppObject.Invoke(template);
//Проверяем наличие Shortcode в полях объекта из БД
var properties = dbObjectInEsppObject.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{ {
var value = property.GetValue(dbObjectInEsppObject)?.ToString(); logger.LogWarning($"Найден объект в ЕСПП с именем шаблона {esppObject.TemplateName} незарегистрированный в ПАРР.");
if (value != null && shortcodesService.isAnyShortcodes(value)) return;
property.SetValue(dbObjectInEsppObject, await shortcodesService.ApplyShortcodesAsync(value, template.UnitId, template.JobId));
}
bool isChanged = false;
//TODO: FIX ME Please, BRO
//bool isChanged;
// если в БД isActive == false, то синхронизировать только по полям из IEsppObject
if (dbObjectInEsppObject.IsActive == false)
{
logger.LogDebug($"Объект деактивирован в ПАРР. Сравниваем только обязательные поля. {esppObject.TemplateName}");
var lightDbObj = new EsppLightObject(dbObjectInEsppObject);
var lightEsppObject = new EsppLightObject(esppObject);
isChanged = IsChanged(lightEsppObject, lightDbObj);
} }
else else
{ {
logger.LogDebug($"Объект активирован в ПАРР. Сравниваем все поля. {esppObject.TemplateName}"); var dbObjectInEsppObject = converterToEsppObject.Invoke(template);
isChanged = IsChanged(esppObject, dbObjectInEsppObject);
}
if (isChanged) //Проверяем наличие Shortcode в полях объекта из БД
{ var properties = dbObjectInEsppObject.GetType().GetProperties();
logger.LogInformation($"Есть изменения, требуется обновление. {esppObject.TemplateName}");
var config = robotConfigurationService.GetFromTemplateByRobotCode(esppObject.Robot, ref template); foreach (PropertyInfo property in properties)
// Если предыдущий статус был Create или Ок, то ставим ему Update
// пусть даже Create завершился с ошибками, но раз он уже есть в ЕСПП, то изменим на Update, сбросим все счетчики и пусть попробует обновить и исправить все
if (config.TaskStatusCode != (int)TaskStatusEnum.Updating)
{ {
SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot); var value = property.GetValue(dbObjectInEsppObject)?.ToString();
if (!await templateService.CommitAsync()) if (value != null && shortcodesService.isAnyShortcodes(value))
logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}"); property.SetValue(dbObjectInEsppObject, await shortcodesService.ApplyShortcodesAsync(value, template.UnitId, template.JobId));
else }
logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}");
bool isChanged = false;
//TODO: FIX ME Please, BRO
//bool isChanged;
// если в БД isActive == false, то синхронизировать только по полям из IEsppObject
if (dbObjectInEsppObject.IsActive == false)
{
logger.LogDebug($"Объект деактивирован в ПАРР. Сравниваем только обязательные поля. {esppObject.TemplateName}");
var lightDbObj = new EsppLightObject(dbObjectInEsppObject);
var lightEsppObject = new EsppLightObject(esppObject);
isChanged = IsChanged(lightEsppObject, lightDbObj);
} }
else else
{ {
// Если пред статус был Update, то ничего не делаем, так его и оставляем, не сбрасывам кол-во попыток и ошибок logger.LogDebug($"Объект активирован в ПАРР. Сравниваем все поля. {esppObject.TemplateName}");
logger.LogInformation($"Есть изменения в Template {template.Name}, но предыдущий статус TaskStatusCode: {(TaskStatusEnum)config.TaskStatusCode}. Не меняем статус, будем разбираться вручную."); isChanged = IsChanged(esppObject, dbObjectInEsppObject);
} }
#region Old logic if (isChanged)
//SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot);
//if (!await templateService.CommitAsync())
// logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}");
//else
// logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}");
#endregion
}//надо ли проверять если не изменился, но был статус Updating не понятно. Доверяем роботу пока, что после окончания работ он точно сообщит
else
{
logger.LogInformation($"Нет изменений, обновление не требуется. {esppObject.TemplateName}");
//если все поля совпали
//проверяем, какой был статус предыдущий статус в БД, если он был не Ок, то ставим ему ОК
var robotConfig = robotConfigurationService.GetFromTemplateByRobotCode(esppObject.Robot, ref template);
if (robotConfig.TaskStatusCode != (int)TaskStatusEnum.Ok)
{ {
robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Ok, ref robotConfig); logger.LogInformation($"Есть изменения, требуется обновление. {esppObject.TemplateName}");
if (!await templateService.CommitAsync())
logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}"); var config = robotConfigurationService.GetFromTemplateByRobotCode(esppObject.Robot, ref template);
// Если предыдущий статус был Create или Ок, то ставим ему Update
// пусть даже Create завершился с ошибками, но раз он уже есть в ЕСПП, то изменим на Update, сбросим все счетчики и пусть попробует обновить и исправить все
if (config.TaskStatusCode != (int)TaskStatusEnum.Updating)
{
SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot);
if (!await templateService.CommitAsync())
logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}");
else
logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}");
}
else else
logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Ok}, Template {template.Name}, Robot: {esppObject.Robot}"); {
// Если пред статус был Update, то ничего не делаем, так его и оставляем, не сбрасывам кол-во попыток и ошибок
logger.LogInformation($"Есть изменения в Template {template.Name}, но предыдущий статус TaskStatusCode: {(TaskStatusEnum)config.TaskStatusCode}. Не меняем статус, будем разбираться вручную.");
}
#region Old logic
//SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot);
//if (!await templateService.CommitAsync())
// logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}");
//else
// logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}");
#endregion
}//надо ли проверять если не изменился, но был статус Updating не понятно. Доверяем роботу пока, что после окончания работ он точно сообщит
else
{
logger.LogInformation($"Нет изменений, обновление не требуется. {esppObject.TemplateName}");
//если все поля совпали
//проверяем, какой был статус предыдущий статус в БД, если он был не Ок, то ставим ему ОК
var robotConfig = robotConfigurationService.GetFromTemplateByRobotCode(esppObject.Robot, ref template);
if (robotConfig.TaskStatusCode != (int)TaskStatusEnum.Ok)
{
robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Ok, ref robotConfig);
if (!await templateService.CommitAsync())
logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}");
else
logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Ok}, Template {template.Name}, Robot: {esppObject.Robot}");
}
} }
} }
} }
} catch (Exception ex)
catch (Exception ex) {
{ logger.LogError(ex, $"Ошибка синхронизации объекта АСУ ЕСПП {esppObject.TemplateName}");
logger.LogError(ex, $"Ошибка синхронизации объекта АСУ ЕСПП {esppObject.TemplateName}"); }
} }
} }

View File

@@ -2,7 +2,7 @@
{ {
public interface ITemplateSyncer public interface ITemplateSyncer
{ {
void Start(); Task StartAsync();
void Stop(); Task StopAsync();
} }
} }

View File

@@ -17,6 +17,7 @@ namespace PARR.EsppTemplateSync.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
internal class StorageSettings internal class StorageSettings

View File

@@ -39,7 +39,7 @@ namespace PARR.EsppTemplateSync
filterExtensions = string.Join(",", storageSettings.EsppTemplates!.AllowedExtensions).Replace(".", "*."); filterExtensions = string.Join(",", storageSettings.EsppTemplates!.AllowedExtensions).Replace(".", "*.");
} }
public void Start() public void StartAsync()
{ {
//смотрим, доступна ли папка //смотрим, доступна ли папка
if (!Directory.Exists(dirPath)) if (!Directory.Exists(dirPath))
@@ -80,7 +80,7 @@ namespace PARR.EsppTemplateSync
} }
public void Stop() public void StopAsync()
{ {
//watcher?.Dispose(); //watcher?.Dispose();
logger.LogInformation($"=== === === FileWatcher остановлен === === ==="); logger.LogInformation($"=== === === FileWatcher остановлен === === ===");
@@ -132,5 +132,14 @@ namespace PARR.EsppTemplateSync
//} //}
} }
Task ITemplateSyncer.StartAsync()
{
throw new NotImplementedException();
}
Task ITemplateSyncer.StopAsync()
{
throw new NotImplementedException();
}
} }
} }

View File

@@ -15,21 +15,25 @@ namespace PARR.EsppTemplateSync
private readonly GlobalSettings globalSettings; private readonly GlobalSettings globalSettings;
private readonly IMqService mqService; private readonly IMqService mqService;
private readonly SettingsFromDb settingsFromDb; private readonly SettingsFromDb settingsFromDb;
private readonly IServiceProvider serviceProvider; private readonly ISyncService<EsppObjectTemplate> syncService;
//private readonly IServiceProvider serviceProvider;
public TemplateMQSyncer( public TemplateMQSyncer(
ILogger<TemplateMQSyncer> logger, ILogger<TemplateMQSyncer> logger,
GlobalSettings globalSettings, GlobalSettings globalSettings,
IMqService mqService, IMqService mqService,
SettingsFromDb settingsFromDb, SettingsFromDb settingsFromDb,
IServiceProvider serviceProvider ISyncService<EsppObjectTemplate> syncService
//IServiceProvider serviceProvider
) )
{ {
this.logger = logger; this.logger = logger;
this.globalSettings = globalSettings; this.globalSettings = globalSettings;
this.mqService = mqService; this.mqService = mqService;
this.settingsFromDb = settingsFromDb; this.settingsFromDb = settingsFromDb;
this.serviceProvider = serviceProvider; this.syncService = syncService;
//this.serviceProvider = serviceProvider;
if (globalSettings.MqSettings == null) if (globalSettings.MqSettings == null)
{ {
logger.LogError("Нет секции настроек хранилища. MqSettings, EsppTemplates"); logger.LogError("Нет секции настроек хранилища. MqSettings, EsppTemplates");
@@ -37,10 +41,10 @@ namespace PARR.EsppTemplateSync
} }
} }
public void Start() public async Task StartAsync()
{ {
var isConnected = mqService.InitConsumer(globalSettings!.MqSettings!, SyncTemplateAsync); var isConnected = await mqService.InitConsumerAsync(globalSettings!.MqSettings!, SyncTemplateAsync);
if (!isConnected) if (!isConnected)
throw new Exception("Ошибка при подключении к RabbitMq"); throw new Exception("Ошибка при подключении к RabbitMq");
@@ -48,9 +52,9 @@ namespace PARR.EsppTemplateSync
logger.LogInformation($"Запущена проверка очереди {globalSettings.MqSettings!.QueueName}."); logger.LogInformation($"Запущена проверка очереди {globalSettings.MqSettings!.QueueName}.");
} }
public void Stop() public async Task StopAsync()
{ {
mqService.Dispose(); await mqService.DisposeAsync();
logger.LogInformation($"=== === === Соединение с очередью {globalSettings.MqSettings!.QueueName} закрыто === === ==="); logger.LogInformation($"=== === === Соединение с очередью {globalSettings.MqSettings!.QueueName} закрыто === === ===");
} }
@@ -58,16 +62,16 @@ namespace PARR.EsppTemplateSync
private async Task SyncTemplateAsync(string str) private async Task SyncTemplateAsync(string str)
{ {
using (var scope = serviceProvider.CreateScope()) //using (var scope = serviceProvider.CreateScope())
{ //{
var syncService = scope.ServiceProvider.GetService<ISyncService<EsppObjectTemplate>>(); // var syncService = scope.ServiceProvider.GetService<ISyncService<EsppObjectTemplate>>();
if (syncService == null) // if (syncService == null)
throw new Exception($"Не смог получить серивс {nameof(ITemplateSyncer)}"); // throw new Exception($"Не смог получить серивс {nameof(ITemplateSyncer)}");
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj);
//}
await syncService.SyncEsppObjectAsync(str, ParseStrToEsppObject, ConvertDbObjToEsppObj);
}
} }

View File

@@ -20,12 +20,12 @@ namespace PARR.EsppTemplateSyncWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
templateSyncer.Start(); await templateSyncer.StartAsync();
} }
public override Task StopAsync(CancellationToken cancellationToken) public override Task StopAsync(CancellationToken cancellationToken)
{ {
templateSyncer.Stop(); templateSyncer.StopAsync().Wait();
return base.StopAsync(cancellationToken); return base.StopAsync(cancellationToken);
} }
} }

View File

@@ -39,7 +39,8 @@
"HostName": "parr-rabbitmq", "HostName": "parr-rabbitmq",
"QueueName": "parr-espp-templates", "QueueName": "parr-espp-templates",
"User": "espp_templates_reader", "User": "espp_templates_reader",
"Password": "P@ssReaderPtk202!" "Password": "P@ssReaderPtk202!",
"PrefetchCount": 100
}, },
"StorageSettings": { "StorageSettings": {
"CheckIntervalSeconds": 30 "CheckIntervalSeconds": 30

View File

@@ -6,6 +6,7 @@ using PARR.BLL.Services.Interfaces;
using PARR.DAL.Models; using PARR.DAL.Models;
using PARR.GeneratorTemplates.Services; using PARR.GeneratorTemplates.Services;
using PARR.GeneratorTemplates.Settings; using PARR.GeneratorTemplates.Settings;
using System.Threading.Tasks;
namespace PARR.GeneratorTemplates namespace PARR.GeneratorTemplates
{ {
@@ -35,7 +36,7 @@ namespace PARR.GeneratorTemplates
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
} }
public void Start() public async Task StartAsync()
{ {
#region test #region test
// string msg = "{\"WorkId\":\"91219bec-e5f7-4a1a-b4c9-c135035d2498\",\"ApplicationId\":\"13044a1f-fa0c-476f-b501-2b8d06bac9f8\",\"Action\":\"create\",\"Ek\":\"ВРТ*ДВС\",\"StatusEk\":0}"; // string msg = "{\"WorkId\":\"91219bec-e5f7-4a1a-b4c9-c135035d2498\",\"ApplicationId\":\"13044a1f-fa0c-476f-b501-2b8d06bac9f8\",\"Action\":\"create\",\"Ek\":\"ВРТ*ДВС\",\"StatusEk\":0}";
@@ -44,15 +45,15 @@ namespace PARR.GeneratorTemplates
// await GenerateTemplates(msg); // await GenerateTemplates(msg);
#endregion #endregion
var isConnected = mqService.InitConsumer(mqSettings, GenerateTemplates); var isConnected = await mqService.InitConsumerAsync(mqSettings, GenerateTemplates);
if (!isConnected) if (!isConnected)
throw new Exception("Ошибка при подключении к RabbitMq"); throw new Exception("Ошибка при подключении к RabbitMq");
} }
public void Stop() public async Task StopAsync()
{ {
mqService.Dispose(); await mqService.DisposeAsync();
} }

View File

@@ -2,7 +2,7 @@
{ {
public interface IGeneratorTemplate public interface IGeneratorTemplate
{ {
void Start(); Task StartAsync();
void Stop(); Task StopAsync();
} }
} }

View File

@@ -8,5 +8,6 @@ namespace PARR.GeneratorTemplates.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -15,7 +15,7 @@ namespace PARR.GeneratorTemplatesWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
generatorTemplate.Start(); await generatorTemplate.StartAsync();
//while (!stoppingToken.IsCancellationRequested) //while (!stoppingToken.IsCancellationRequested)
//{ //{
// _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); // _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
@@ -25,7 +25,7 @@ namespace PARR.GeneratorTemplatesWorker
public override Task StopAsync(CancellationToken cancellationToken) public override Task StopAsync(CancellationToken cancellationToken)
{ {
generatorTemplate.Stop(); generatorTemplate.StopAsync().Wait();
return base.StopAsync(cancellationToken); return base.StopAsync(cancellationToken);
} }

View File

@@ -87,7 +87,8 @@ namespace PARR.JobAutoControl
{ {
job.JobEkMasks.ToList().ForEach(ek => job.JobEkMasks.ToList().ForEach(ek =>
{ {
job.JobAutoControlInEkStatuses.ToList().ForEach(ekStatus => //todo: проверить, нормально ли отрабатывает async в Foreach
job.JobAutoControlInEkStatuses.ToList().ForEach(async ekStatus =>
{ {
var request = new GeneratorTemplateMq var request = new GeneratorTemplateMq
{ {
@@ -103,7 +104,7 @@ namespace PARR.JobAutoControl
var msg = JsonSerializer.Serialize(request); var msg = JsonSerializer.Serialize(request);
var result = mqService.Send(mqSettings.Generator, new[] { msg }); var result =await mqService.SendAsync(mqSettings.Generator, new[] { msg });
if (result.IsSuccess) if (result.IsSuccess)
logger.LogInformation($"Отправлен запрос на генерацию: {msg}"); logger.LogInformation($"Отправлен запрос на генерацию: {msg}");
@@ -135,7 +136,8 @@ namespace PARR.JobAutoControl
if (!jobsToDeactivate.Any()) if (!jobsToDeactivate.Any())
return; return;
jobsToDeactivate.ForEach(applicationInWorkId => //todo: проверить, нормально ли отрабатывает async в foreach
jobsToDeactivate.ForEach(async applicationInWorkId =>
{ {
var request = new TemplateActivatorMq var request = new TemplateActivatorMq
{ {
@@ -146,7 +148,7 @@ namespace PARR.JobAutoControl
var msg = JsonSerializer.Serialize(request); var msg = JsonSerializer.Serialize(request);
var result = mqService.Send(mqSettings.Activator, new[] { msg }); var result = await mqService.SendAsync(mqSettings.Activator, new[] { msg });
if (result.IsSuccess) if (result.IsSuccess)
logger.LogInformation($"Отправлен запрос на деактивацию: {msg}"); logger.LogInformation($"Отправлен запрос на деактивацию: {msg}");
@@ -174,7 +176,8 @@ namespace PARR.JobAutoControl
if (!jobsToActivate.Any()) if (!jobsToActivate.Any())
return; return;
jobsToActivate.ForEach(applicationInWorkId => //todo: проверить, нормально ли отрабатывает async в foreach
jobsToActivate.ForEach(async applicationInWorkId =>
{ {
var request = new TemplateActivatorMq var request = new TemplateActivatorMq
{ {
@@ -185,7 +188,7 @@ namespace PARR.JobAutoControl
var msg = JsonSerializer.Serialize(request); var msg = JsonSerializer.Serialize(request);
var result = mqService.Send(mqSettings.Activator, new[] { msg }); var result = await mqService.SendAsync(mqSettings.Activator, new[] { msg });
if (result.IsSuccess) if (result.IsSuccess)
logger.LogInformation($"Отправлен запрос на активацию: {msg}"); logger.LogInformation($"Отправлен запрос на активацию: {msg}");

View File

@@ -14,6 +14,7 @@ namespace PARR.JobAutoControl.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
internal class Activator : IMqSettings internal class Activator : IMqSettings
@@ -22,6 +23,7 @@ namespace PARR.JobAutoControl.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }

View File

@@ -66,7 +66,7 @@ namespace PARR.Master.Services
if (objs.Any()) if (objs.Any())
{ {
var sendResult = mqService.Send(mqSettings, objs.ToArray()); var sendResult = await mqService.SendAsync(mqSettings, objs.ToArray());
if (sendResult.IsSuccess) if (sendResult.IsSuccess)
{ {
logger.LogInformation($"Наряды переданы в RabbitMQ: {objs.Count()}"); logger.LogInformation($"Наряды переданы в RabbitMQ: {objs.Count()}");

View File

@@ -8,5 +8,6 @@ namespace PARR.Master.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -6,44 +6,45 @@ namespace PARR.MockData
{ {
internal class EsppDataImporter internal class EsppDataImporter
{ {
public void Import() { public void Import()
var factory = new ConnectionFactory() { HostName = "10.99.253.216" }; {
factory.UserName = "espp_templates_robot"; //var factory = new ConnectionFactory() { HostName = "10.99.253.216" };
factory.Password = "P@ssRwEsppTempl202"; //factory.UserName = "espp_templates_robot";
//factory.Password = "P@ssRwEsppTempl202";
string[] rows = EsppExport.data.Replace("\r", "").Split('\n'); //string[] rows = EsppExport.data.Replace("\r", "").Split('\n');
using (var connection = factory.CreateConnection()) //using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel()) //using (var channel = connection.CreateModel())
{ //{
//https://www.rabbitmq.com/lazy-queues.html // //https://www.rabbitmq.com/lazy-queues.html
//Рекомендовано при работе порциями использовать ленивые очереди. сообщения не используют память // //Рекомендовано при работе порциями использовать ленивые очереди. сообщения не используют память
//Формируем соответствующий аргумент // //Формируем соответствующий аргумент
Dictionary<String, Object> argums = new Dictionary<String, Object>(); // Dictionary<String, Object> argums = new Dictionary<String, Object>();
argums.Add("x-queue-mode", "lazy"); // argums.Add("x-queue-mode", "lazy");
//Объявляем очередь с которой будем работать. Если такой очереди ещё нет, то создатся. Если нет, нужно параметры типа durable должны совпадать иначе будет ошибка. В целом эти параметры можно посмотреть в админке RabbitMQ // //Объявляем очередь с которой будем работать. Если такой очереди ещё нет, то создатся. Если нет, нужно параметры типа durable должны совпадать иначе будет ошибка. В целом эти параметры можно посмотреть в админке RabbitMQ
channel.QueueDeclare(queue: "parr-espp-templates", // channel.QueueDeclare(queue: "parr-espp-templates",
durable: true,//If you want to make sure your messages are persisted, you need to create a durable queue. To do this, simply set the durable flag to true when creating the queue. // durable: true,//If you want to make sure your messages are persisted, you need to create a durable queue. To do this, simply set the durable flag to true when creating the queue.
exclusive: false, // exclusive: false,
autoDelete: false, // autoDelete: false,
arguments: argums); // arguments: argums);
//перебираем строки и отправляем строки сообщениями // //перебираем строки и отправляем строки сообщениями
for (int i = 0; i < rows.Count(); i++) // for (int i = 0; i < rows.Count(); i++)
{ // {
var body = Encoding.UTF8.GetBytes(rows[i]); // var body = Encoding.UTF8.GetBytes(rows[i]);
IBasicProperties props = channel.CreateBasicProperties(); // IBasicProperties props = channel.CreateBasicProperties();
//If you dont mark your messages as persistent, then RabbitMQ will store them in memory. This is fine if youre just playing around or building a prototype. But if youre building a production system, then you need to make sure that your messages are persisted to disk so that theyre not lost if the server crashes. // //If you dont mark your messages as persistent, then RabbitMQ will store them in memory. This is fine if youre just playing around or building a prototype. But if youre building a production system, then you need to make sure that your messages are persisted to disk so that theyre not lost if the server crashes.
//Говорим менеджеру RabbitMq при получении хранить сообщения на диске // //Говорим менеджеру RabbitMq при получении хранить сообщения на диске
props.DeliveryMode = 2; // props.DeliveryMode = 2;
//время жизни в мс. на этапе отладки сделаем коротким чтобы не устраивать помойку // //время жизни в мс. на этапе отладки сделаем коротким чтобы не устраивать помойку
props.Expiration = "60000"; // props.Expiration = "60000";
channel.BasicPublish(exchange: "", // channel.BasicPublish(exchange: "",
routingKey: "parr-espp-templates",//совпадает с именем очереди иначе не принимает // routingKey: "parr-espp-templates",//совпадает с именем очереди иначе не принимает
basicProperties: props, // basicProperties: props,
body: body); // body: body);
} // }
} //}
} }
} }

View File

@@ -10,7 +10,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="RabbitMQ.Client" Version="6.5.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" /> <PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" /> <PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />

View File

@@ -2,6 +2,6 @@
public interface IMqTemplateActivator public interface IMqTemplateActivator
{ {
void Start(); Task StartAsync();
void Stop(); Task StopAsync();
} }

View File

@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using PARR.BLL.Domain.Mq; using PARR.BLL.Domain.Mq;
using PARR.BLL.Services.Interfaces; using PARR.BLL.Services.Interfaces;
using PARR.Constants; using PARR.Constants;
using System.Threading.Tasks;
namespace PARR.TemplateActivator; namespace PARR.TemplateActivator;
@@ -31,9 +32,9 @@ internal class MqTemplateActivator : IMqTemplateActivator
this.validatorService = validatorService; this.validatorService = validatorService;
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
} }
public void Start() public async Task StartAsync()
{ {
var isConnected = mqService.InitConsumer(mqSettings, UpdateTemplateStatusAsync); var isConnected = await mqService.InitConsumerAsync(mqSettings, UpdateTemplateStatusAsync);
if (!isConnected) if (!isConnected)
throw new Exception("Ошибка при подключении к RabbitMq"); throw new Exception("Ошибка при подключении к RabbitMq");
@@ -64,8 +65,8 @@ internal class MqTemplateActivator : IMqTemplateActivator
} }
} }
public void Stop() public async Task StopAsync()
{ {
mqService.Dispose(); await mqService.DisposeAsync();
} }
} }

View File

@@ -8,5 +8,6 @@ internal class MqSettings : IMqSettings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }

View File

@@ -15,12 +15,12 @@ namespace PARR.TemplateActivatorWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
mqTemplateActivator.Start(); await mqTemplateActivator.StartAsync();
} }
public override Task StopAsync(CancellationToken cancellationToken) public override Task StopAsync(CancellationToken cancellationToken)
{ {
mqTemplateActivator.Stop(); mqTemplateActivator.StopAsync().Wait();
return base.StopAsync(cancellationToken); return base.StopAsync(cancellationToken);
} }

View File

@@ -2,7 +2,7 @@
{ {
public interface IMqTemplateDistributor public interface IMqTemplateDistributor
{ {
void Start(); Task StartAsync();
void Stop(); Task StopAsync();
} }
} }

View File

@@ -4,6 +4,7 @@ using PARR.BLL.Domain.Mq;
using PARR.BLL.Services.Interfaces; using PARR.BLL.Services.Interfaces;
using PARR.TemplateDistributor.Services; using PARR.TemplateDistributor.Services;
using PARR.TemplateDistributor.Settings; using PARR.TemplateDistributor.Settings;
using System.Threading.Tasks;
namespace PARR.TemplateDistributor namespace PARR.TemplateDistributor
{ {
@@ -31,18 +32,18 @@ namespace PARR.TemplateDistributor
this.validatorService = validatorService; this.validatorService = validatorService;
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
} }
public void Start() public async Task StartAsync()
{ {
var isConnected = mqService.InitConsumer(mqSettings, UpdateScheduleAsync); var isConnected = await mqService.InitConsumerAsync(mqSettings, UpdateScheduleAsync);
if (!isConnected) if (!isConnected)
throw new Exception("Ошибка при подключении к RabbitMq"); throw new Exception("Ошибка при подключении к RabbitMq");
} }
public void Stop() public async Task StopAsync()
{ {
mqService.Dispose(); await mqService.DisposeAsync();
} }
private async Task UpdateScheduleAsync(string msg) private async Task UpdateScheduleAsync(string msg)

View File

@@ -8,5 +8,6 @@ namespace PARR.TemplateDistributor.Settings
public string QueueName { get; set; } = string.Empty; public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty; public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
} }
} }

View File

@@ -15,12 +15,12 @@ namespace PARR.TemplateDistributorWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
mqTemplateDistributor.Start(); await mqTemplateDistributor.StartAsync();
} }
public override Task StopAsync(CancellationToken cancellationToken) public override Task StopAsync(CancellationToken cancellationToken)
{ {
mqTemplateDistributor.Stop(); mqTemplateDistributor.StopAsync().Wait();
return base.StopAsync(cancellationToken); return base.StopAsync(cancellationToken);
} }

View File

@@ -35,19 +35,16 @@ namespace PARR.TemplateGeneratorWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
while (!stoppingToken.IsCancellationRequested) var isConnected = await mqService.InitConsumerAsync(mqSettings, GenerateTemplateAsync);
{
var isConnected = mqService.InitConsumer(mqSettings, GenerateTemplateAsync);
if (!isConnected) if (!isConnected)
throw new Exception("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> RabbitMq"); throw new Exception("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> RabbitMq");
}
} }
public override Task StopAsync(CancellationToken cancellationToken) public override Task StopAsync(CancellationToken cancellationToken)
{ {
mqService.Dispose(); mqService.DisposeAsync().AsTask().Wait();
return base.StopAsync(cancellationToken); return base.StopAsync(cancellationToken);
} }