public async Task OnMessageAsync(MessageSession session, BrokeredMessage message) { if (message.Label != null && message.ContentType != null && message.Label.Equals("RecipeStep", StringComparison.InvariantCultureIgnoreCase) && message.ContentType.Equals("application/json", StringComparison.InvariantCultureIgnoreCase)) { var body = message.GetBody <Stream>(); dynamic recipeStep = JsonConvert.DeserializeObject(new StreamReader(body, true).ReadToEnd()); lock (Console.Out) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine( "\t\t\t\tMessage received: \n\t\t\t\t\t\tSessionId = {0}, \n\t\t\t\t\t\tMessageId = {1}, \n\t\t\t\t\t\tSequenceNumber = {2}," + "\n\t\t\t\t\t\tContent: [ step = {3}, title = {4} ]", message.SessionId, message.MessageId, message.SequenceNumber, recipeStep.step, recipeStep.title); Console.ResetColor(); } await message.CompleteAsync(); if (recipeStep.step == 5) { // end of the session! await session.CloseAsync(); } } else { await message.DeadLetterAsync("BadMessage", "Unexpected message"); } }
public async Task OnMessageAsync(MessageSession session, BrokeredMessage message) { if (message.Label != null && message.ContentType != null && message.Label.Equals("RecipeStep", StringComparison.InvariantCultureIgnoreCase) && message.ContentType.Equals("application/json", StringComparison.InvariantCultureIgnoreCase)) { var body = message.GetBody<Stream>(); dynamic recipeStep = JsonConvert.DeserializeObject(new StreamReader(body, true).ReadToEnd()); lock (Console.Out) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine( "\t\t\t\tMessage received: \n\t\t\t\t\t\tSessionId = {0}, \n\t\t\t\t\t\tMessageId = {1}, \n\t\t\t\t\t\tSequenceNumber = {2}," + "\n\t\t\t\t\t\tContent: [ step = {3}, title = {4} ]", message.SessionId, message.MessageId, message.SequenceNumber, recipeStep.step, recipeStep.title); Console.ResetColor(); } await message.CompleteAsync(); if (recipeStep.step == 5) { // end of the session! await session.CloseAsync(); } } else { await message.DeadLetterAsync("BadMessage", "Unexpected message"); } }
/// <summary> /// Receives the messages in an asynchronous loop and closes the session once there are no more messages. /// </summary> private void ReceiveMessagesAndCloseSession(MessageSession session, CancellationToken cancellationToken) { CountdownEvent unreleasedMessages = new CountdownEvent(1); Action <bool> closeSession = (bool success) => { Action doClose = () => { try { unreleasedMessages.Signal(); if (!unreleasedMessages.Wait(15000, cancellationToken)) { Trace.TraceWarning("Waited for pending unreleased messages before closing session in subscription {0} but they did not complete in time", this.subscription); } } catch (OperationCanceledException) { } finally { unreleasedMessages.Dispose(); } this.receiveRetryPolicy.ExecuteAsync(() => session.CloseAsync().ContinueWith(s => { if (s.Exception != null) { Exception ex = s.Exception; this.instrumentation.SessionEnded(); Trace.TraceError("An unrecoverable error occurred while trying to close a session in subscription {1}:\r\n{0}", ex, this.subscription); this.dynamicThrottling.NotifyWorkCompletedWithError(); } else { this.instrumentation.SessionEnded(); if (success) { this.dynamicThrottling.NotifyWorkCompleted(); } else { this.dynamicThrottling.NotifyWorkCompletedWithError(); } } })); }; if (this.requiresSequentialProcessing) { doClose.Invoke(); } else { // Allow some time for releasing the messages before closing. Also, continue in a non I/O completion thread in order to block. TaskEx.Delay(200).ContinueWith(t => doClose()); } }; // Declare an action to receive the next message in the queue or closes the session if cancelled. Action receiveNext = null; // Declare an action acting as a callback whenever a non-transient exception occurs while receiving or processing messages. Action <Exception> recoverReceive = null; // Declare an action responsible for the core operations in the message receive loop. Action receiveMessage = (() => { // Use a retry policy to execute the Receive action in an asynchronous and reliable fashion. this.receiveRetryPolicy.ExecuteAsync(() => session.ReceiveAsync(TimeSpan.Zero).ContinueWith(m => { if (m.Exception != null) { recoverReceive.Invoke(m.Exception); } else { BrokeredMessage msg = m.Result; if (msg != null) { var roundtripStopwatch = Stopwatch.StartNew(); long schedulingElapsedMilliseconds = 0; long processingElapsedMilliseconds = 0; unreleasedMessages.AddCount(); Task.Factory.StartNew(() => { var releaseAction = MessageReleaseAction.AbandonMessage; try { this.instrumentation.MessageReceived(); schedulingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds; // Make sure the process was told to stop receiving while it was waiting for a new message. if (!cancellationToken.IsCancellationRequested) { try { try { // Process the received message. releaseAction = this.InvokeMessageHandler(msg); processingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds - schedulingElapsedMilliseconds; this.instrumentation.MessageProcessed(releaseAction.Kind == MessageReleaseActionKind.Complete, processingElapsedMilliseconds); } catch { processingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds - schedulingElapsedMilliseconds; this.instrumentation.MessageProcessed(false, processingElapsedMilliseconds); throw; } } finally { if (roundtripStopwatch.Elapsed > TimeSpan.FromSeconds(45)) { this.dynamicThrottling.Penalize(); } } } } finally { // Ensure that any resources allocated by a BrokeredMessage instance are released. if (this.requiresSequentialProcessing) { this.ReleaseMessage(msg, releaseAction, () => { receiveNext(); }, () => { closeSession(false); }, unreleasedMessages, processingElapsedMilliseconds, schedulingElapsedMilliseconds, roundtripStopwatch); } else { // Receives next without waiting for the message to be released. this.ReleaseMessage(msg, releaseAction, () => { }, () => { this.dynamicThrottling.Penalize(); }, unreleasedMessages, processingElapsedMilliseconds, schedulingElapsedMilliseconds, roundtripStopwatch); receiveNext.Invoke(); } } }); } else { // no more messages in the session, close it and do not continue receiving closeSession(true); } } })); }); // Initialize an action to receive the next message in the queue or closes the session if cancelled. receiveNext = () => { if (!cancellationToken.IsCancellationRequested) { // Continue receiving and processing new messages until told to stop. receiveMessage.Invoke(); } else { closeSession(true); } }; // Initialize a custom action acting as a callback whenever a non-transient exception occurs while receiving or processing messages. recoverReceive = ex => { // Just log an exception. Do not allow an unhandled exception to terminate the message receive loop abnormally. Trace.TraceError("An unrecoverable error occurred while trying to receive a new message from subscription {1}:\r\n{0}", ex, this.subscription); // Cannot continue to receive messages from this session. closeSession(false); }; // Start receiving messages asynchronously for the session. receiveNext.Invoke(); }
/// <summary> /// Receives the messages in an asynchronous loop and closes the session once there are no more messages. /// </summary> private void ReceiveMessagesAndCloseSession(MessageSession session, CancellationToken cancellationToken) { CountdownEvent unreleasedMessages = new CountdownEvent(1); Action<bool> closeSession = (bool success) => { Action doClose = () => { try { unreleasedMessages.Signal(); if (!unreleasedMessages.Wait(15000, cancellationToken)) { Trace.TraceWarning("Waited for pending unreleased messages before closing session in subscription {0} but they did not complete in time", this.subscription); } } catch (OperationCanceledException) { } finally { unreleasedMessages.Dispose(); } this.receiveRetryPolicy.ExecuteAsync(() => session.CloseAsync().ContinueWith(s => { if (s.Exception != null) { Exception ex = s.Exception; this.instrumentation.SessionEnded(); Trace.TraceError("An unrecoverable error occurred while trying to close a session in subscription {1}:\r\n{0}", ex, this.subscription); this.dynamicThrottling.NotifyWorkCompletedWithError(); } else { this.instrumentation.SessionEnded(); if (success) { this.dynamicThrottling.NotifyWorkCompleted(); } else { this.dynamicThrottling.NotifyWorkCompletedWithError(); } } })); }; if (this.requiresSequentialProcessing) { doClose.Invoke(); } else { // Allow some time for releasing the messages before closing. Also, continue in a non I/O completion thread in order to block. TaskEx.Delay(200).ContinueWith(t => doClose()); } }; // Declare an action to receive the next message in the queue or closes the session if cancelled. Action receiveNext = null; // Declare an action acting as a callback whenever a non-transient exception occurs while receiving or processing messages. Action<Exception> recoverReceive = null; // Declare an action responsible for the core operations in the message receive loop. Action receiveMessage = (() => { // Use a retry policy to execute the Receive action in an asynchronous and reliable fashion. this.receiveRetryPolicy.ExecuteAsync(() => session.ReceiveAsync(TimeSpan.Zero).ContinueWith(m => { if (m.Exception != null) { recoverReceive.Invoke(m.Exception); } else { BrokeredMessage msg = m.Result; if (msg != null) { var roundtripStopwatch = Stopwatch.StartNew(); long schedulingElapsedMilliseconds = 0; long processingElapsedMilliseconds = 0; unreleasedMessages.AddCount(); Task.Factory.StartNew(() => { var releaseAction = MessageReleaseAction.AbandonMessage; try { this.instrumentation.MessageReceived(); schedulingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds; // Make sure the process was told to stop receiving while it was waiting for a new message. if (!cancellationToken.IsCancellationRequested) { try { try { // Process the received message. releaseAction = this.InvokeMessageHandler(msg); processingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds - schedulingElapsedMilliseconds; this.instrumentation.MessageProcessed(releaseAction.Kind == MessageReleaseActionKind.Complete, processingElapsedMilliseconds); } catch { processingElapsedMilliseconds = roundtripStopwatch.ElapsedMilliseconds - schedulingElapsedMilliseconds; this.instrumentation.MessageProcessed(false, processingElapsedMilliseconds); throw; } } finally { if (roundtripStopwatch.Elapsed > TimeSpan.FromSeconds(45)) { this.dynamicThrottling.Penalize(); } } } } finally { // Ensure that any resources allocated by a BrokeredMessage instance are released. if (this.requiresSequentialProcessing) { this.ReleaseMessage(msg, releaseAction, () => { receiveNext(); }, () => { closeSession(false); }, unreleasedMessages, processingElapsedMilliseconds, schedulingElapsedMilliseconds, roundtripStopwatch); } else { // Receives next without waiting for the message to be released. this.ReleaseMessage(msg, releaseAction, () => { }, () => { this.dynamicThrottling.Penalize(); }, unreleasedMessages, processingElapsedMilliseconds, schedulingElapsedMilliseconds, roundtripStopwatch); receiveNext.Invoke(); } } }); } else { // no more messages in the session, close it and do not continue receiving closeSession(true); } } })); }); // Initialize an action to receive the next message in the queue or closes the session if cancelled. receiveNext = () => { if (!cancellationToken.IsCancellationRequested) { // Continue receiving and processing new messages until told to stop. receiveMessage.Invoke(); } else { closeSession(true); } }; // Initialize a custom action acting as a callback whenever a non-transient exception occurs while receiving or processing messages. recoverReceive = ex => { // Just log an exception. Do not allow an unhandled exception to terminate the message receive loop abnormally. Trace.TraceError("An unrecoverable error occurred while trying to receive a new message from subscription {1}:\r\n{0}", ex, this.subscription); // Cannot continue to receive messages from this session. closeSession(false); }; // Start receiving messages asynchronously for the session. receiveNext.Invoke(); }