private void QueueWaitingCheckTask() { //ensure queues are processed if queue count is < _maxfetchcount after a time elasped of no messages incoming (waiting) Task.Run(async() => { while (true) { try { if (DateTime.UtcNow.Subtract(LastMessageTime).TotalSeconds > 5) { try { await semaphoreSlim.WaitAsync(); var processingCollection = ClientMessages.Take((int)MaxFetchCount).ToDictionary(k => k.Key, v => v.Value); if (!(Model?.IsClosed).GetValueOrDefault()) { BasicGetResult messageResult = null; try { messageResult = Model?.BasicGet(QueueName, false); } catch (Exception ex) { Debug.WriteLine(ex.ToString(), "QueueWaitingCheckTask => BasicGet"); queueWaitErrorAction?.Invoke(ex); } //flush the queue until we reach max or null while (messageResult != null & processingCollection.Count < (int)MaxFetchCount) { var message = BasicPropertiesHelper.DecodeMessage <TMessage>(messageResult.BasicProperties, messageResult.Body); if (!processingCollection.ContainsKey(messageResult.DeliveryTag)) { processingCollection.Add(messageResult.DeliveryTag, ProcessingMessage.Add(messageResult.DeliveryTag, message)); } messageResult = Model?.BasicGet(QueueName, false); } } if (processingCollection?.Count > 0) { if (!Model.IsClosed) { //both of these should not be implemented one or the other //if single message handler is created var processedMessage = processingCollection.Values.Select(v => (TMessage)v.Message).FirstOrDefault(); if (processedMessage != null) { if (messageFunctionAsync != null) { await messageFunctionAsync?.Invoke(processedMessage); } if (messageFunction != null) { messageFunction?.Invoke(processedMessage); } } if (processingCollection?.Values != null) { var messages = processingCollection.Values?.Where(v => v.Message != null).Select(v => (TMessage)v.Message); //if multi messagehandler is created if (multipleMessagesFunctionAsync != null) { await multipleMessagesFunctionAsync?.Invoke(messages); } if (multipleMessagesFunction != null) { multipleMessagesFunction?.Invoke(messages); } } } //ack the messages foreach (var processing in processingCollection) { //remove processed message if (!Model.IsClosed) { ClientMessages.Remove(processing.Key); Model.BasicAck(processing.Key, false); } } } } finally { LastMessageTime = DateTime.UtcNow; semaphoreSlim.Release(); } } await Task.Delay(100); } catch (Exception ex) { Debug.WriteLine(ex.ToString(), "QueueWaitingCheckTask"); queueWaitErrorAction?.Invoke(ex); } } //while }).ConfigureAwait(false); //Task.Run }
/// <summary> /// Called each time a message arrives for this consumer. /// </summary> /// <remarks> /// </remarks> public override void HandleBasicDeliver( string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties properties, byte[] body) { TMessage message = default; Task.Run(async() => { try { if ((Model?.IsClosed).GetValueOrDefault()) { return; } message = BasicPropertiesHelper.DecodeMessage <TMessage>(properties, body); if (message == null) { //wrong message shouldn't be in this queue so remove it without requeue //not in the ClientMessages collection yet Model.BasicReject(deliveryTag, false); } //process the messages once we hit the fetch count try { await semaphoreSlim.WaitAsync(); if (MaxFetchCount > 1) { //add a message to process ClientMessages.Add(deliveryTag, ProcessingMessage.Add(deliveryTag, message)); if (ClientMessages.Count == MaxFetchCount) { var processingCollection = ClientMessages.Take((int)MaxFetchCount).ToDictionary(k => k.Key, v => v.Value); var toProcessMessages = processingCollection?.Where(v => v.Value.Message != null)?.Select(v => (TMessage)v.Value.Message); //execute work action if (multipleMessagesFunctionAsync != null) { await multipleMessagesFunctionAsync?.Invoke(toProcessMessages); } if (multipleMessagesFunction != null) { multipleMessagesFunction?.Invoke(toProcessMessages); } foreach (var processing in processingCollection) { if (!Model.IsClosed) { ClientMessages.Remove(processing.Key); //ack the message Model.BasicAck(processing.Key, false); } } } if (ClientMessages.Count > MaxFetchCount) { if (!Model.IsClosed) { ClientMessages.Remove(deliveryTag); //reject and requeue message we are at max fetch count Model.BasicReject(deliveryTag, true); } } } else //process messages one at a time { if (!Model.IsClosed) { //if maxfetch is <= 1 then process and ack a single message if (messageFunctionAsync != null) { await messageFunctionAsync?.Invoke(message); } if (messageFunction != null) { messageFunction?.Invoke(message); } if (multipleMessagesFunctionAsync != null) { await multipleMessagesFunctionAsync?.Invoke(new TMessage[] { message }); } if (multipleMessagesFunction != null) { multipleMessagesFunction?.Invoke(new TMessage[] { message }); } ClientMessages.Remove(deliveryTag); Model.BasicAck(deliveryTag, false); } } } finally { LastMessageTime = DateTime.UtcNow; semaphoreSlim.Release(); } } catch (Exception ex) { //Model.BasicNack(deliveryTag, false, false); Debug.WriteLine(ex.ToString(), "HandleBasicDeliver"); errorAction?.Invoke(ex, message); } }); }