private static async Task ReceiveMessagesAsync(string subscriptionName) { await using ServiceBusReceiver subscriptionReceiver = s_client.CreateReceiver( TopicName, subscriptionName, new ServiceBusReceiverOptions { ReceiveMode = ServiceBusReceiveMode.ReceiveAndDelete }); Console.WriteLine($"=========================================================================="); Console.WriteLine($"{DateTime.Now} :: Receiving Messages From Subscription: {subscriptionName}"); int receivedMessageCount = 0; while (true) { var receivedMessage = await subscriptionReceiver.ReceiveMessageAsync(TimeSpan.FromSeconds(1)); if (receivedMessage != null) { PrintReceivedMessage(receivedMessage); receivedMessageCount++; } else { break; } } Console.WriteLine($"{DateTime.Now} :: Received '{receivedMessageCount}' Messages From Subscription: {subscriptionName}"); Console.WriteLine($"=========================================================================="); await subscriptionReceiver.CloseAsync(); }
public async Task StopReceive(CancellationToken cancellationToken = default) { messageReceivingCancellationTokenSource?.Cancel(); using (cancellationToken.Register(() => messageProcessingCancellationTokenSource?.Cancel())) { await messageReceivingTask.ConfigureAwait(false); while (concurrencyLimiter.CurrentCount != maxConcurrency) { // Do not forward cancellationToken here so that pump has ability to exit gracefully // Individual message processing pipelines will be canceled instead await Task.Delay(50, CancellationToken.None).ConfigureAwait(false); } try { await receiver.CloseAsync(cancellationToken).ConfigureAwait(false); } catch (Exception ex) when(ex.IsCausedBy(cancellationToken)) { Logger.Debug($"Operation canceled while stopping the receiver {receiver.EntityPath}.", ex); } } concurrencyLimiter?.Dispose(); messageReceivingCancellationTokenSource?.Dispose(); messageProcessingCancellationTokenSource?.Dispose(); circuitBreaker?.Dispose(); messageReceivingTask = null; }
public static async Task Main(string[] args) { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appSettings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables(); IConfigurationRoot Configuration = builder.Build(); var publisherConnectionString = Configuration.GetConnectionString("SubscriberConnectionString"); var topicName = Configuration["TopicName"]; Console.WriteLine(topicName); var serviceBusPubClient = new ServiceBusClient(publisherConnectionString); var serviceBusReceiverOptions = new ServiceBusReceiverOptions() { ReceiveMode = ServiceBusReceiveMode.ReceiveAndDelete }; var subNameA = "sub-a"; var subNameB = "sub-b"; ServiceBusReceiver serviceBusReceiverSubA = serviceBusPubClient.CreateReceiver(topicName, subNameA, serviceBusReceiverOptions); ServiceBusReceiver serviceBusReceiverSubB = serviceBusPubClient.CreateReceiver(topicName, subNameB, serviceBusReceiverOptions); IList <Task> receivers = new List <Task>(); receivers.Add(ReceivedMessagesAsync(serviceBusReceiverSubA, subNameA)); receivers.Add(ReceivedMessagesAsync(serviceBusReceiverSubB, subNameB)); await Task.WhenAll(receivers); await serviceBusReceiverSubA.CloseAsync(); await serviceBusReceiverSubB.CloseAsync(); Console.WriteLine("All messages received"); Console.ReadLine(); }
public async Task RoundRobinSessions() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { await using var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.GetSender(scope.QueueName); var messageCt = 10; HashSet <string> sessions = new HashSet <string>() { "1", "2", "3" }; // send the messages foreach (string session in sessions) { using ServiceBusMessageBatch batch = await sender.CreateBatchAsync(); ServiceBusMessageBatch messageBatch = AddMessages(batch, messageCt, session); await sender.SendBatchAsync(messageBatch); } // create receiver not scoped to a specific session for (int i = 0; i < 10; i++) { ServiceBusReceiver receiver = await client.GetSessionReceiverAsync(scope.QueueName); foreach (ServiceBusMessage peekedMessage in await receiver.PeekBatchBySequenceAsync( fromSequenceNumber: 1, maxMessages: 10)) { var sessionId = receiver.SessionManager.SessionId; Assert.AreEqual(sessionId, peekedMessage.SessionId); } // Close the receiver client when we are done with it. Since the sessionClient doesn't own the underlying connection, the connection remains open, but the session link will be closed. await receiver.CloseAsync(); } } }
private async Task RunBatchReceiveLoopAsync(CancellationToken cancellationToken) { ServiceBusClient sessionClient = null; ServiceBusReceiver receiver = null; if (_isSessionsEnabled) { sessionClient = _client.Value; } else { receiver = _batchReceiver.Value; } // The batch receive loop below only executes functions at a concurrency level of 1, // so we don't need to do anything special when DynamicConcurrency is enabled. If in // the future we make this loop concurrent, we'll have to check with ConcurrencyManager. while (true) { try { if (cancellationToken.IsCancellationRequested) { _logger.LogInformation($"Message processing has been stopped or cancelled ({_details.Value})"); return; } if (_isSessionsEnabled && (receiver == null || receiver.IsClosed)) { try { receiver = await sessionClient.AcceptNextSessionAsync( _entityPath, new ServiceBusSessionReceiverOptions { PrefetchCount = _serviceBusOptions.PrefetchCount }, cancellationToken).ConfigureAwait(false); } catch (ServiceBusException ex) when(ex.Reason == ServiceBusFailureReason.ServiceTimeout) { // it's expected if the entity is empty, try next time continue; } } IReadOnlyList <ServiceBusReceivedMessage> messages = await receiver.ReceiveMessagesAsync( _serviceBusOptions.MaxMessageBatchSize, cancellationToken : cancellationToken).AwaitWithCancellation(cancellationToken); if (messages.Count > 0) { var messageActions = _isSessionsEnabled ? new ServiceBusSessionMessageActions((ServiceBusSessionReceiver)receiver) : new ServiceBusMessageActions(receiver); var receiveActions = new ServiceBusReceiveActions(receiver); ServiceBusReceivedMessage[] messagesArray = messages.ToArray(); ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateBatch( messagesArray, messageActions, receiveActions, _client.Value); FunctionResult result = await _triggerExecutor.TryExecuteAsync(input.GetTriggerFunctionData(), cancellationToken).ConfigureAwait(false); receiveActions.EndExecutionScope(); var processedMessages = messagesArray.Concat(receiveActions.Messages.Keys); // Complete batch of messages only if the execution was successful if (_autoCompleteMessages && result.Succeeded) { List <Task> completeTasks = new List <Task>(); foreach (ServiceBusReceivedMessage message in processedMessages) { // skip messages that were settled in the user's function if (input.MessageActions.SettledMessages.ContainsKey(message)) { continue; } // Pass CancellationToken.None to allow autocompletion to finish even when shutting down completeTasks.Add(receiver.CompleteMessageAsync(message, CancellationToken.None)); } await Task.WhenAll(completeTasks).ConfigureAwait(false); } else if (!result.Succeeded) { // For failed executions, we abandon the messages regardless of the autoCompleteMessages configuration. // This matches the behavior that happens for single dispatch functions as the processor does the same thing // in the Service Bus SDK. List <Task> abandonTasks = new List <Task>(); foreach (ServiceBusReceivedMessage message in processedMessages) { // skip messages that were settled in the user's function if (input.MessageActions.SettledMessages.ContainsKey(message)) { continue; } // Pass CancellationToken.None to allow abandon to finish even when shutting down abandonTasks.Add(receiver.AbandonMessageAsync(message, cancellationToken: CancellationToken.None)); } await Task.WhenAll(abandonTasks).ConfigureAwait(false); } if (_isSessionsEnabled) { if (((ServiceBusSessionMessageActions)messageActions).ShouldReleaseSession) { // Use CancellationToken.None to attempt to close the receiver even when shutting down await receiver.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } } else { // Close the session and release the session lock after draining all messages for the accepted session. if (_isSessionsEnabled) { // Use CancellationToken.None to attempt to close the receiver even when shutting down await receiver.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } } catch (ObjectDisposedException) { // Ignore as we are stopping the host } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { // Ignore as we are stopping the host _logger.LogInformation($"Message processing has been stopped or cancelled ({_details.Value})"); } catch (Exception ex) { // Log another exception _logger.LogError(ex, $"An unhandled exception occurred in the message batch receive loop ({_details.Value})"); if (_isSessionsEnabled && receiver != null) { // Attempt to close the session and release session lock to accept a new session on the next loop iteration try { // Use CancellationToken.None to attempt to close the receiver even when shutting down await receiver.CloseAsync(CancellationToken.None).ConfigureAwait(false); } catch { // Best effort receiver = null; } } } } }
async Task <long> DoPurgeNonSessionEntity(long messagesToPurgeCount, bool purgeDeadLetterSubqueueInstead) { long totalMessagesPurged = 0; var taskCount = Math.Min((int)messagesToPurgeCount / 1000 + 1, 20); var tasks = new Task[taskCount]; var quit = false; // This instance controls all the receiving tasks var client = new ServiceBusClient( serviceBusHelper.ConnectionString, new ServiceBusClientOptions { TransportType = serviceBusHelper.TransportType }); try { for (var taskIndex = 0; taskIndex < tasks.Length; taskIndex++) { tasks[taskIndex] = Task.Run(async() => { ServiceBusReceiver receiver = CreateServiceBusReceiver( client, purgeDeadLetterSubqueueInstead); try { var consecutiveZeroBatchReceives = 0; const int enoughZeroBatchReceives = 3; while (!quit && Interlocked.Read(ref totalMessagesPurged) < messagesToPurgeCount) { IEnumerable <ServiceBusReceivedMessage> messages; messages = await receiver.ReceiveMessagesAsync( maxMessages: 1000, maxWaitTime: TimeSpan.FromMilliseconds(20000 * (consecutiveZeroBatchReceives + 1))) .ConfigureAwait(false); // ReSharper disable once PossibleMultipleEnumeration if (messages != null && messages.Any()) { // ReSharper disable once PossibleMultipleEnumeration long messageCount = messages.Count(); Interlocked.Add(ref totalMessagesPurged, messageCount); } else { ++consecutiveZeroBatchReceives; if (consecutiveZeroBatchReceives >= enoughZeroBatchReceives) { quit = true; } } } } finally { if (null != receiver) { await receiver.CloseAsync().ConfigureAwait(false); } } }); // End of lambda } await Task.WhenAll(tasks).ConfigureAwait(false); } finally { await client.DisposeAsync().ConfigureAwait(false); } return(totalMessagesPurged); }
internal void StartMessageBatchReceiver(CancellationToken cancellationToken) { ServiceBusClient sessionClient = null; ServiceBusReceiver receiver = null; if (_isSessionsEnabled) { sessionClient = _sessionClient.Value; } else { receiver = BatchReceiver; } Task.Run(async() => { while (true) { try { if (!_started || cancellationToken.IsCancellationRequested) { _logger.LogInformation("Message processing has been stopped or cancelled"); return; } if (_isSessionsEnabled && (receiver == null || receiver.IsClosed)) { try { receiver = await sessionClient.AcceptNextSessionAsync(_entityPath, new ServiceBusSessionReceiverOptions { PrefetchCount = _serviceBusOptions.PrefetchCount }).ConfigureAwait(false); } catch (ServiceBusException ex) when(ex.Reason == ServiceBusFailureReason.ServiceTimeout) { // it's expected if the entity is empty, try next time continue; } } IReadOnlyList <ServiceBusReceivedMessage> messages = await receiver.ReceiveMessagesAsync(_serviceBusOptions.MaxMessages).ConfigureAwait(false); if (messages != null) { ServiceBusReceivedMessage[] messagesArray = messages.ToArray(); ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateBatch(messagesArray); if (_isSessionsEnabled) { input.MessageActions = new ServiceBusSessionMessageActions((ServiceBusSessionReceiver)receiver); } else { input.MessageActions = new ServiceBusMessageActions(receiver); } FunctionResult result = await _triggerExecutor.TryExecuteAsync(input.GetTriggerFunctionData(), cancellationToken).ConfigureAwait(false); if (cancellationToken.IsCancellationRequested) { return; } // Complete batch of messages only if the execution was successful if (_serviceBusOptions.AutoCompleteMessages && _started) { if (result.Succeeded) { List <Task> completeTasks = new List <Task>(); foreach (ServiceBusReceivedMessage message in messagesArray) { completeTasks.Add(receiver.CompleteMessageAsync(message)); } await Task.WhenAll(completeTasks).ConfigureAwait(false); } else { List <Task> abandonTasks = new List <Task>(); foreach (ServiceBusReceivedMessage message in messagesArray) { abandonTasks.Add(receiver.AbandonMessageAsync(message)); } await Task.WhenAll(abandonTasks).ConfigureAwait(false); } } } else { // Close the session and release the session lock after draining all messages for the accepted session. if (_isSessionsEnabled) { await receiver.CloseAsync().ConfigureAwait(false); } } } catch (ObjectDisposedException) { // Ignore as we are stopping the host } catch (Exception ex) { // Log another exception _logger.LogError(ex, $"An unhandled exception occurred in the message batch receive loop"); if (_isSessionsEnabled && receiver != null) { // Attempt to close the session and release session lock to accept a new session on the next loop iteration try { await receiver.CloseAsync().ConfigureAwait(false); } catch { // Best effort receiver = null; } } } } }, cancellationToken); }
private async Task RunBatchReceiveLoopAsync(CancellationToken cancellationToken) { ServiceBusClient sessionClient = null; ServiceBusReceiver receiver = null; if (_isSessionsEnabled) { sessionClient = _client.Value; } else { receiver = _batchReceiver.Value; } while (true) { try { if (cancellationToken.IsCancellationRequested) { _logger.LogInformation("Message processing has been stopped or cancelled"); return; } if (_isSessionsEnabled && (receiver == null || receiver.IsClosed)) { try { receiver = await sessionClient.AcceptNextSessionAsync( _entityPath, new ServiceBusSessionReceiverOptions { PrefetchCount = _serviceBusOptions.PrefetchCount }, cancellationToken).ConfigureAwait(false); } catch (ServiceBusException ex) when(ex.Reason == ServiceBusFailureReason.ServiceTimeout) { // it's expected if the entity is empty, try next time continue; } } IReadOnlyList <ServiceBusReceivedMessage> messages = await receiver.ReceiveMessagesAsync( _serviceBusOptions.MaxMessageBatchSize, cancellationToken : cancellationToken).AwaitWithCancellation(cancellationToken); if (messages.Count > 0) { ServiceBusReceivedMessage[] messagesArray = messages.ToArray(); ServiceBusTriggerInput input = ServiceBusTriggerInput.CreateBatch(messagesArray); if (_isSessionsEnabled) { input.MessageActions = new ServiceBusSessionMessageActions((ServiceBusSessionReceiver)receiver); } else { input.MessageActions = new ServiceBusMessageActions(receiver); } FunctionResult result = await _triggerExecutor.TryExecuteAsync(input.GetTriggerFunctionData(), cancellationToken).ConfigureAwait(false); // Complete batch of messages only if the execution was successful if (_autoCompleteMessagesOptionEvaluatedValue) { if (result.Succeeded) { List <Task> completeTasks = new List <Task>(); foreach (ServiceBusReceivedMessage message in messagesArray) { // skip messages that were settled in the user's function if (input.MessageActions.SettledMessages.Contains(message)) { continue; } // Pass CancellationToken.None to allow autocompletion to finish even when shutting down completeTasks.Add(receiver.CompleteMessageAsync(message, CancellationToken.None)); } await Task.WhenAll(completeTasks).ConfigureAwait(false); } else { List <Task> abandonTasks = new List <Task>(); foreach (ServiceBusReceivedMessage message in messagesArray) { // skip messages that were settled in the user's function if (input.MessageActions.SettledMessages.Contains(message)) { continue; } // Pass CancellationToken.None to allow abandon to finish even when shutting down abandonTasks.Add(receiver.AbandonMessageAsync(message, cancellationToken: CancellationToken.None)); } await Task.WhenAll(abandonTasks).ConfigureAwait(false); } } } else { // Close the session and release the session lock after draining all messages for the accepted session. if (_isSessionsEnabled) { // Use CancellationToken.None to attempt to close the receiver even when shutting down await receiver.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } } catch (ObjectDisposedException) { // Ignore as we are stopping the host } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { // Ignore as we are stopping the host _logger.LogInformation("Message processing has been stopped or cancelled"); } catch (Exception ex) { // Log another exception _logger.LogError(ex, $"An unhandled exception occurred in the message batch receive loop"); if (_isSessionsEnabled && receiver != null) { // Attempt to close the session and release session lock to accept a new session on the next loop iteration try { // Use CancellationToken.None to attempt to close the receiver even when shutting down await receiver.CloseAsync(CancellationToken.None).ConfigureAwait(false); } catch { // Best effort receiver = null; } } } } }
public void Close() { s_logger.LogWarning("Closing the MessageReceiver connection"); _messageReceiver.CloseAsync().GetAwaiter().GetResult(); s_logger.LogWarning("MessageReceiver connection stopped"); }