/// <summary> /// Receives a batch of <see cref="ServiceBusMessage" /> from the Service Bus entity partition. /// </summary> /// /// <param name="maximumMessageCount">The maximum number of messages to receive in this batch.</param> /// <param name="timeout"></param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>The batch of <see cref="ServiceBusMessage" /> from the Service Bus entity partition this consumer is associated with. If no events are present, an empty enumerable is returned.</returns> /// internal async Task <IEnumerable <ServiceBusMessage> > ReceiveAsyncInternal( int maximumMessageCount, TimeSpan timeout, CancellationToken cancellationToken) { Argument.AssertNotClosed(_closed, nameof(AmqpConsumer)); Argument.AssertAtLeast(maximumMessageCount, 1, nameof(maximumMessageCount)); var link = default(ReceivingAmqpLink); var amqpMessages = default(IEnumerable <AmqpMessage>); var receivedMessages = default(List <ServiceBusMessage>); var stopWatch = Stopwatch.StartNew(); ServiceBusEventSource.Log.MessageReceiveStart(EntityName); link = await ReceiveLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, timeout)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var messagesReceived = await Task.Factory.FromAsync ( (callback, state) => link.BeginReceiveRemoteMessages(maximumMessageCount, TimeSpan.FromMilliseconds(20), timeout, callback, state), (asyncResult) => link.EndReceiveMessages(asyncResult, out amqpMessages), TaskCreationOptions.RunContinuationsAsynchronously ).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // If event messages were received, then package them for consumption and // return them. if ((messagesReceived) && (amqpMessages != null)) { receivedMessages = new List <ServiceBusMessage>(); foreach (AmqpMessage message in amqpMessages) { if (_receiveMode == ReceiveMode.ReceiveAndDelete) { link.DisposeDelivery(message, true, AmqpConstants.AcceptedOutcome); } receivedMessages.Add(AmqpMessageConverter.AmqpMessageToSBMessage(message)); // message.Dispose(); } stopWatch.Stop(); return(receivedMessages); } stopWatch.Stop(); // No events were available. return(Enumerable.Empty <ServiceBusMessage>()); }
/// <summary> /// Get the session Id corresponding to this consumer /// </summary> /// <param name="cancellationToken"></param> /// <returns></returns> public override async Task <string> GetSessionId(CancellationToken cancellationToken = default) { if (!_isSessionReceiver) { return(null); } ReceivingAmqpLink openedLink = null; await _retryPolicy.RunOperation( async (timeout) => openedLink = await ReceiveLink.GetOrCreateAsync(timeout).ConfigureAwait(false), EntityName, ConnectionScope, cancellationToken).ConfigureAwait(false); var source = (Source)openedLink.Settings.Source; source.FilterSet.TryGetValue <string>(AmqpClientConstants.SessionFilterName, out var sessionId); return(sessionId); }
/// <summary> /// Closes the connection to the transport consumer instance. /// </summary> /// /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// public override async Task CloseAsync(CancellationToken cancellationToken) { if (_closed) { return; } _closed = true; var clientId = GetHashCode().ToString(); var clientType = GetType(); try { ServiceBusEventSource.Log.ClientCloseStart(clientType, EntityName, clientId); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); if (ReceiveLink?.TryGetOpenedObject(out var _) == true) { cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); await ReceiveLink.CloseAsync().ConfigureAwait(false); } ReceiveLink?.Dispose(); } catch (Exception ex) { _closed = false; ServiceBusEventSource.Log.ClientCloseError(clientType, EntityName, clientId, ex.Message); throw; } finally { ServiceBusEventSource.Log.ClientCloseComplete(clientType, EntityName, clientId); } }
internal override async Task <ReceivingAmqpLink> GetOrCreateLinkAsync(TimeSpan timeout) { return(await ReceiveLink.GetOrCreateAsync(timeout).ConfigureAwait(false)); }
/// <summary> /// Completes a series of <see cref="ServiceBusMessage"/> using a list of lock tokens. This will delete the message from the service. /// </summary> /// /// <param name="lockTokens">An <see cref="IEnumerable{T}"/> containing the lock tokens of the corresponding messages to complete.</param> /// <param name="outcome"></param> /// <param name="timeout"></param> internal override async Task DisposeMessagesAsync( IEnumerable <Guid> lockTokens, Outcome outcome, TimeSpan timeout) { if (_isSessionReceiver) { // TODO - ThrowIfSessionLockLost(); } List <ArraySegment <byte> > deliveryTags = ConvertLockTokensToDeliveryTags(lockTokens); ReceivingAmqpLink receiveLink = null; try { ArraySegment <byte> transactionId = AmqpConstants.NullBinary; //var ambientTransaction = Transaction.Current; //if (ambientTransaction != null) //{ // transactionId = await AmqpTransactionManager.Instance.EnlistAsync(ambientTransaction, ServiceBusConnection).ConfigureAwait(false); //} if (!ReceiveLink.TryGetOpenedObject(out receiveLink)) { receiveLink = await ReceiveLink.GetOrCreateAsync(timeout).ConfigureAwait(false); } var disposeMessageTasks = new Task <Outcome> [deliveryTags.Count]; var i = 0; foreach (ArraySegment <byte> deliveryTag in deliveryTags) { disposeMessageTasks[i++] = Task.Factory.FromAsync( (c, s) => receiveLink.BeginDisposeMessage(deliveryTag, transactionId, outcome, true, timeout, c, s), a => receiveLink.EndDisposeMessage(a), this); } var outcomes = await Task.WhenAll(disposeMessageTasks).ConfigureAwait(false); Error error = null; foreach (Outcome item in outcomes) { var disposedOutcome = item.DescriptorCode == Rejected.Code && ((error = ((Rejected)item).Error) != null) ? item : null; if (disposedOutcome != null) { if (error.Condition.Equals(AmqpErrorCode.NotFound)) { if (_isSessionReceiver) { // throw new SessionLockLostException(Resources.SessionLockExpiredOnMessageSession); } // throw new MessageLockLostException(Resources.MessageLockLost); } // throw error.ToMessagingContractException(); } } } catch (Exception exception) { if (exception is OperationCanceledException && receiveLink != null && receiveLink.State != AmqpObjectState.Opened) { // The link state is lost, We need to return a non-retriable error. // MessagingEventSource.Log.LinkStateLost(ClientId, receiveLink.Name, receiveLink.State, isSessionReceiver, exception); if (_isSessionReceiver) { // throw new SessionLockLostException(Resources.SessionLockExpiredOnMessageSession); } // throw new MessageLockLostException(Resources.MessageLockLost); } // throw AmqpExceptionHelper.GetClientException(exception); throw; } }
/// <summary> /// Receives a batch of <see cref="ServiceBusMessage" /> from the Service Bus entity partition. /// </summary> /// /// <param name="maximumMessageCount">The maximum number of messages to receive in this batch.</param> /// <param name="maximumWaitTime">The maximum amount of time to wait to build up the requested message count for the batch; if not specified, the per-try timeout specified by the retry policy will be used.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>The batch of <see cref="ServiceBusMessage" /> from the Service Bus entity partition this consumer is associated with. If no events are present, an empty enumerable is returned.</returns> /// public override async Task <IEnumerable <ServiceBusMessage> > ReceiveAsync( int maximumMessageCount, TimeSpan?maximumWaitTime, CancellationToken cancellationToken) { Argument.AssertNotClosed(_closed, nameof(AmqpConsumer)); Argument.AssertAtLeast(maximumMessageCount, 1, nameof(maximumMessageCount)); var receivedMessageCount = 0; var failedAttemptCount = 0; var tryTimeout = _retryPolicy.CalculateTryTimeout(0); var waitTime = (maximumWaitTime ?? tryTimeout); var link = default(ReceivingAmqpLink); var retryDelay = default(TimeSpan?); var amqpMessages = default(IEnumerable <AmqpMessage>); var receivedMessages = default(List <ServiceBusMessage>); var stopWatch = Stopwatch.StartNew(); try { while (!cancellationToken.IsCancellationRequested) { try { ServiceBusEventSource.Log.MessageReceiveStart(EntityName); link = await ReceiveLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, tryTimeout)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var messagesReceived = await Task.Factory.FromAsync ( (callback, state) => link.BeginReceiveRemoteMessages(maximumMessageCount, TimeSpan.FromMilliseconds(20), waitTime, callback, state), (asyncResult) => link.EndReceiveMessages(asyncResult, out amqpMessages), TaskCreationOptions.RunContinuationsAsynchronously ).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // If event messages were received, then package them for consumption and // return them. if ((messagesReceived) && (amqpMessages != null)) { receivedMessages ??= new List <ServiceBusMessage>(); foreach (AmqpMessage message in amqpMessages) { //link.DisposeDelivery(message, true, AmqpConstants.AcceptedOutcome); receivedMessages.Add(AmqpMessageConverter.AmqpMessageToSBMessage(message)); message.Dispose(); } receivedMessageCount = receivedMessages.Count; return(receivedMessages); } // No events were available. return(Enumerable.Empty <ServiceBusMessage>()); } catch (ServiceBusException ex) when(ex.Reason == ServiceBusException.FailureReason.ServiceTimeout) { // Because the timeout specified with the request is intended to be the maximum // amount of time to wait for events, a timeout isn't considered an error condition, // rather a sign that no events were available in the requested period. return(Enumerable.Empty <ServiceBusMessage>()); } catch (Exception ex) { Exception activeEx = ex.TranslateServiceException(EntityName); // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop. // Otherwise, bubble the exception. ++failedAttemptCount; retryDelay = _retryPolicy.CalculateRetryDelay(activeEx, failedAttemptCount); if ((retryDelay.HasValue) && (!ConnectionScope.IsDisposed) && (!cancellationToken.IsCancellationRequested)) { ServiceBusEventSource.Log.MessageReceiveError(EntityName, activeEx.Message); await Task.Delay(UseMinimum(retryDelay.Value, waitTime.CalculateRemaining(stopWatch.Elapsed)), cancellationToken).ConfigureAwait(false); tryTimeout = _retryPolicy.CalculateTryTimeout(failedAttemptCount); } else if (ex is AmqpException) { throw activeEx; } else { throw; } } } // If no value has been returned nor exception thrown by this point, // then cancellation has been requested. throw new TaskCanceledException(); } catch (Exception ex) { ServiceBusEventSource.Log.MessageReceiveError(EntityName, ex.Message); throw; } finally { stopWatch.Stop(); ServiceBusEventSource.Log.MessageReceiveComplete(EntityName, receivedMessageCount); } }