/// <summary> /// Задача по актуализации /// </summary> private void Actualize() { // Убираем из текущих слушателей остановленные var stoppedConsumers = this._consumers.Where(x => !x.IsRunning); this._consumers.RemoveAll(x => stoppedConsumers.Contains(x)); var subscriptions = _esbSubscriptionsManager.GetCallbackSubscriptions(); var allConsumers = subscriptions.Select(x => new RmqConsumer(_logger, _converter, _connectionFactory, x, DefaultPrefetchCount, useLegacySenders)).ToList(); // Множество новых слушатей = Текущие подписки - запущенные подписки var newConsumers = allConsumers.Except(this._consumers); // Множество слушателей на удаление = Запущенные подписки - текущие подписки var consumersToDelete = this._consumers.Except(allConsumers); foreach (var newConsumer in newConsumers) { this._consumers.Add(newConsumer); newConsumer.Start(); } foreach (var consumerToDelete in consumersToDelete) { consumerToDelete.Stop(); } // Обновляем данные подписки (на случай если изменился тип callback'а или адрес) foreach (var consumer in this._consumers) { var actualConsumer = allConsumers.First(x => x.Equals(consumer)); consumer.UpdateSubscription(actualConsumer.Subscription); } }
/// <summary> /// Метод, выполняющий запуск потоков отправки для каждой подписки. /// </summary> private void SendMsgsByCallBack() { try { // Ищем актуальные подписки, для которых есть сообщения. IEnumerable <Subscription> callbackSubscriptions = _subscriptionsManager.GetCallbackSubscriptions().Where(x => GetCurrentMessageCount(x.Client.ID, x.MessageType.ID) > 0); try { foreach (var subscription in callbackSubscriptions) { SubscriberThreadPool.QueueUserWorkItem(subscription, _scanninWaitEvent, _statistics, _dataService, _logger); } SubscriberThreadPool.CheckForUnActiveSubscribers(); } catch (Exception e) { _logger.LogUnhandledException(e, null, "Ошибка при порождении потоков подписчиков через QueueUserWorkItem."); } } catch (Exception e) { _logger.LogUnhandledException(e, null, "Ошибка при получении активных подписок, отправляемых по Callback."); } }
/// <summary> /// Selects messages from the database that can be sent. /// </summary> /// <param name="state">Not used.</param> private void ScanMessages(object state) { try { if (_sendingTasksCount >= MaxTasks) { return; } IEnumerable <Subscription> subscriptions = _subscriptionsManager.GetCallbackSubscriptions(); if (!subscriptions.Any()) { return; } foreach (var clientSubscriptions in subscriptions.GroupBy(s => s.Client)) { int currentConnections = 0; int connectionsLimit = clientSubscriptions.Key.ConnectionsLimit ?? DefaultConnectionsLimit; Guid clientId = (KeyGuid)clientSubscriptions.Key.__PrimaryKey; if (!_clientConnections.TryGetValue(clientId, out currentConnections)) { _clientConnections.Add(clientId, 0); } if (currentConnections >= connectionsLimit) { continue; } SQLWhereLanguageDef langDef = SQLWhereLanguageDef.LanguageDef; LoadingCustomizationStruct lcs = LoadingCustomizationStruct.GetSimpleStruct(typeof(Message), Message.Views.SendingByCallbackView); // All messages for this client by all its active subscriptions Function clientLimitFunction = langDef.GetFunction(langDef.funcOR, clientSubscriptions.Select(s => langDef.GetFunction( langDef.funcAND, langDef.GetFunction(langDef.funcEQ, new VariableDef(langDef.GuidType, Information.ExtractPropertyPath <Message>(m => m.Recipient)), s.Client.__PrimaryKey), langDef.GetFunction(langDef.funcEQ, new VariableDef(langDef.GuidType, Information.ExtractPropertyPath <Message>(m => m.MessageType)), s.MessageType.__PrimaryKey))).ToArray()); // Only unsent messages whose sending time has already arrived lcs.LimitFunction = langDef.GetFunction( langDef.funcAND, clientLimitFunction, langDef.GetFunction(langDef.funcEQ, new VariableDef(langDef.BoolType, Information.ExtractPropertyPath <Message>(m => m.IsSending)), false), langDef.GetFunction(langDef.funcLEQ, new VariableDef(langDef.DateTimeType, Information.ExtractPropertyPath <Message>(m => m.SendingTime)), DateTime.Now)); // Get no more than we can send lcs.ReturnTop = connectionsLimit - currentConnections; lcs.ColumnsSort = new[] { new ColumnsSortDef(Information.ExtractPropertyPath <Message>(m => m.Priority), SortOrder.Asc), new ColumnsSortDef(Information.ExtractPropertyPath <Message>(m => m.SendingTime), SortOrder.Asc), }; Stopwatch stopwatch = Stopwatch.StartNew(); DataObject[] messages = _dataService.LoadObjects(lcs); stopwatch.Stop(); _statisticsService.NotifyAvgTimeSql(null, (int)stopwatch.ElapsedMilliseconds, $"PrioritySendingManager.ScanMessages(): Load {lcs.ReturnTop} messages for client with name: {clientSubscriptions.Key.Name}."); int index = 0; while (index < messages.Length && TryEnqueue((Message)messages[index])) { index++; } } } catch (Exception exception) { _logger.LogError("An error occurred while scanning messages.", exception.ToString()); } }
/// <summary> /// Метод отправки сообщений из БД, выполняющийся по таймеру. /// </summary> /// <param name="state">Состояние, передаваемое при вызове метода таймером.</param> private void ScanMessages(object state) { try { if (_sendingTasksCount >= MaxTasks) { return; } IEnumerable <Subscription> subscriptions = _subscriptionsManager.GetCallbackSubscriptions().ToList(); if (!subscriptions.Any()) { return; } // Условие, ограничивающее получателя и тип сообщения загружаемых сообщений в соответствии с имеющимися подписками. Function[] conditionsList = subscriptions .Select( s => _langDef.GetFunction( _langDef.funcAND, _langDef.GetFunction(_langDef.funcEQ, new VariableDef(_langDef.GuidType, Information.ExtractPropertyPath <Message>(x => x.Recipient)), s.Client.__PrimaryKey), _langDef.GetFunction(_langDef.funcEQ, new VariableDef(_langDef.GuidType, Information.ExtractPropertyPath <Message>(x => x.MessageType)), s.MessageType.__PrimaryKey))) .ToArray(); Function subscriptionsCondition = _langDef.GetFunction(_langDef.funcOR, conditionsList); Function messagesLf = _langDef.GetFunction( _langDef.funcAND, subscriptionsCondition, _langDef.GetFunction(_langDef.funcEQ, new VariableDef(_langDef.BoolType, Information.ExtractPropertyPath <Message>(x => x.IsSending)), false), _langDef.GetFunction(_langDef.funcLEQ, new VariableDef(_langDef.DateTimeType, Information.ExtractPropertyPath <Message>(x => x.SendingTime)), DateTime.Now)); LoadingCustomizationStruct lcs = LoadingCustomizationStruct.GetSimpleStruct(typeof(Message), Message.Views.MessageEditView); lcs.LimitFunction = messagesLf; lcs.ReturnTop = MaxTasks - _sendingTasksCount; lcs.ColumnsSort = new[] { new ColumnsSortDef(Information.ExtractPropertyPath <Message>(x => x.Priority), SortOrder.Asc), new ColumnsSortDef(Information.ExtractPropertyPath <Message>(x => x.SendingTime), SortOrder.Asc) }; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); DataObject[] messages = _dataService.LoadObjects(lcs); stopwatch.Stop(); long time = stopwatch.ElapsedMilliseconds; _statisticsService.NotifyAvgTimeSql(null, (int)time, "OptimizedSendingManager.ScanMessages() load messages."); int index = 0; while (index < messages.Length && TryEnqueue((Message)messages[index])) { index++; } } catch (Exception exception) { _logger.LogError("Send message error", exception.ToString()); } }