/// <summary> /// Process a collection of queue messages. /// </summary> /// <param name="messages">collection of the queue messages</param> private void ProcessMessages(IEnumerable <CloudQueueMessage> messages) { BrokerTracing.TraceInfo(SoaHelper.CreateTraceMessage("Proxy", "ProcessMessages", string.Format("Process {0} messages.", messages.Count <CloudQueueMessage>()))); messages.AsParallel <CloudQueueMessage>() .ForAll <CloudQueueMessage>( (requestQueueMessage) => { Message request = null; try { BrokerTracing.TraceInfo(string.Format("SOA broker proxy perf1 - {0}", DateTime.UtcNow.TimeOfDay.TotalSeconds)); request = this.requestStorageClients.First().GetWcfMessageFromQueueMessage(requestQueueMessage); this.requestMessageQueue.Add(request); // this.requestMessageQueue.Enqueue(request); UniqueId messageId = SoaHelper.GetMessageId(request); BrokerTracing.TraceInfo(SoaHelper.CreateTraceMessage("Proxy", "Request received inqueue", string.Empty, messageId, "Request message in queue")); } catch (Exception e) { BrokerTracing.TraceError(SoaHelper.CreateTraceMessage("Proxy", "ProcessMessages", string.Format("Error occurs {0}", e))); } }); this.semaphoreForWorker.Release(); }
/// <summary> /// Process a collection of queue messages. /// </summary> /// <param name="messages">collection of the queue messages</param> private void ProcessMessages(IEnumerable <CloudQueueMessage> messages) { SessionBase.TraceSource.TraceInformation( SoaHelper.CreateTraceMessage( "Proxy", "ProcessMessages", string.Format("Process {0} messages.", messages.Count <CloudQueueMessage>()))); messages.AsParallel <CloudQueueMessage>().ForAll <CloudQueueMessage>( (responseQueueMessage) => { Message response = null; try { SessionBase.TraceSource.TraceInformation( string.Format("SOA broker proxy perf1 - {0}", DateTime.UtcNow.TimeOfDay.TotalSeconds)); response = this.responseStorageClient.GetWcfMessageFromQueueMessage(responseQueueMessage); var qkey = Tuple.Create(SoaHelper.GetClientDataHeaderFromMessage(response)); if (!this.responseMessageQueues.ContainsKey(qkey)) { lock (this.responseMessageQueues) { if (!this.responseMessageQueues.ContainsKey(qkey)) { this.responseMessageQueues.Add(qkey, new ConcurrentQueue <Message>()); } } } this.responseMessageQueues[qkey].Enqueue(response); UniqueId messageId = SoaHelper.GetMessageId(response); SessionBase.TraceSource.TraceInformation( SoaHelper.CreateTraceMessage( "Proxy", "Response received inqueue", string.Empty, messageId, "Response message in queue")); // do not delete the message from the queue here, depends on the long invisable timeout and hourly cleanup // this.responseStorageClient.DeleteMessageAsync(responseQueueMessage, messageId); } catch (Exception e) { SessionBase.TraceSource.TraceInformation( SoaHelper.CreateTraceMessage( "Proxy", "ProcessMessages", string.Format("Error occurs {0}", e))); } }); this.semaphoreForWorker.Release(); }
public static Guid GetMessageIdFromMessage(Message message) { Guid guid = Guid.Empty; UniqueId id = SoaHelper.GetMessageId(message); if (id != null) { id.TryGetGuid(out guid); } return(guid); }
/// <summary> /// Prepare addressing headers when converting messages /// </summary> /// <param name="source">indicating the source message</param> /// <param name="destination">indicating the destination message</param> public static void PrepareAddressingHeaders(Message source, Message destination) { if (destination.Headers.MessageVersion.Addressing != AddressingVersion.None) { if (source.Headers.MessageVersion.Addressing != AddressingVersion.None) { destination.Headers.MessageId = source.Headers.MessageId; destination.Headers.RelatesTo = source.Headers.RelatesTo; destination.Headers.ReplyTo = source.Headers.ReplyTo; destination.Headers.To = source.Headers.To; destination.Headers.FaultTo = source.Headers.FaultTo; destination.Headers.From = source.Headers.From; } else { UniqueId messageId = SoaHelper.GetMessageId(source); if (messageId != null) { destination.Headers.MessageId = messageId; } } } }
/// <summary> /// Receives response messages from broker's response service /// </summary> /// <param name="message">Response message</param> public void SendResponse(Message message) { try { int currentResponseCount = 0; // Reset the heartbeat since operation just succeeded this.session.ResetHeartbeat(); // Reset timeout timer since a response was received if (this.responseHandlerTimeoutTimer != null) { this.responseHandlerTimeoutTimer.Change(this.responseHanderTimeout, 0); } // Bug #15946: handling client side exception thrown from WebResponseHandler(when talking to rest service) if (string.Equals(message.Headers.Action, Constant.WebAPI_ClientSideException, StringComparison.Ordinal)) { Exception e = message.Properties[Constant.WebAPI_ClientSideException] as Exception; this.InvokeCallback(new BrokerResponse <TMessage>(e, message.Headers.RelatesTo)); return; } // TODO: Consider whether user callback should get an EOM message if (!Utility.IsEOM(message)) { // If the handler is closed, timed out, closed or broker heartbeat signaled, dont forward any more requests to the user. // NOTE: If some responses already slipped through its OK because we prefer that over adding a lock here if (this.shuttingDown) { return; } // Create a BrokerResponse object wrapper for the message object. A copy of the message must be created MessageBuffer messageBuffer = null; try { messageBuffer = message.CreateBufferedCopy(Constant.MaxBufferSize); } catch (Exception e) { Utility.LogError("AsyncResponseCallback.SendResponse received exception - {0}", e); this.InvokeCallback(new BrokerResponse <TMessage>(e, message.Headers.RelatesTo)); return; } BrokerResponse <TMessage> brokerResponse = this.CreateResponse(BrokerClientBase.GetActionFromResponseMessage(message), !message.IsFault ? message.Headers.Action : String.Empty, messageBuffer, message.Headers.RelatesTo == null ? SoaHelper.GetMessageId(message) : message.Headers.RelatesTo); BrokerResponse <TMessage> lastResponse = null; if (this.ignoreIsLastResponseProperty) { lastResponse = brokerResponse; } else { // Atomically swap out the last response. lastResponse = Interlocked.Exchange <BrokerResponse <TMessage> >(ref this.lastBrokerResponse, brokerResponse); } // If there was a previous last response if (lastResponse != null) { // Send it to the callback this.InvokeCallback(lastResponse); // Increment response count currentResponseCount = Interlocked.Increment(ref this.currentResponseCount); } // If the caller wants all messages at once, do so. // Else we always request ResponseWindowSize * 2 messages. If we get to // the end of a window, request another if (this.responseWindowSize != Constant.GetResponse_All && currentResponseCount != 0 && (currentResponseCount + 1) % this.responseWindowSize == 0) { // Call async version and block on completion in order to workaround System.Net.Socket bug #750028 try { SessionBase.TraceSource.TraceInformation("GetResponse : currentResponseCount {0} : clientId {1}", currentResponseCount, this.clientId); this.responseService.GetResponses( this.action, this.callbackManagerId, GetResponsePosition.Current, this.responseWindowSize, this.clientId); } catch (FaultException <SessionFault> e) { throw Utility.TranslateFaultException(e); } } } // If this is a client purged fault, return that exception in a BrokerResponse else { TypedMessageConverter endOfResponsesConverter = TypedMessageConverter.Create(typeof(EndOfResponses), Constant.EndOfMessageAction); EndOfResponses endOfResponses = (EndOfResponses)endOfResponsesConverter.FromMessage(message); switch (endOfResponses.Reason) { case EndOfResponsesReason.ClientPurged: this.InvokeCallback(new BrokerResponse <TMessage>(SessionBase.ClientPurgedException, new UniqueId(Guid.Empty))); break; case EndOfResponsesReason.ClientTimeout: this.InvokeCallback(new BrokerResponse <TMessage>(SessionBase.ClientTimeoutException, new UniqueId(Guid.Empty))); break; default: // Save how many responses are expected (minus 1 for the last response) System.Diagnostics.Debug.Assert(endOfResponses.Count > 0, "Response count should also be positive number"); this.expectedResponseCount = endOfResponses.Count - 1; break; } } if (!this.ignoreIsLastResponseProperty) { // Check to see if we receive the EOM message and all the responses. // If so pass last response to callback marked as such. Also make // sure this isnt called again with a lock. if (this.expectedResponseCount == this.currentResponseCount) { lock (this.lastResponseLock) { if (this.expectedResponseCount == this.currentResponseCount) { // Mark the response as the last one this.lastBrokerResponse.isLastResponse = true; // Send last response to callback. Note since currentResponseCount // is incremented after responses are sent to the callback, the // callback will not be processing any other responses at this time. this.InvokeCallback(this.lastBrokerResponse); this.expectedResponseCount = -1; } } } } } catch (Exception e) { // Log and eat unhandled user exceptions from their calback Utility.LogError("Unhandled exception processing response - {0}", e); } }
/// <summary> /// Receives new response messages from broker /// </summary> /// <param name="response">Response message</param> void IResponseServiceCallback.SendResponse(Message response) { MessageBuffer messageBuffer = null; EndOfResponses endOfResponses = null; Exception clientSideException = null; bool isEOM = false; // Reset the heartbeat since operation just succeeded this.session.ResetHeartbeat(); try { // Check whether this is an EOM message isEOM = Utility.IsEOM(response); if (!isEOM) { // If not EOM, create a copy of the message's buffer so it can be deserialized // A copy is needed because WCF will dispose this message when the callback returns // Alternatively we could deserialize the entire message but we want // callback to be quick and we may wind up deserializing messages the user // never looks at (i.e. just checks user data or exits enum early) try { messageBuffer = response.CreateBufferedCopy(Constant.MaxBufferSize); } catch (Exception e) { SessionBase.TraceSource.TraceEvent(TraceEventType.Error, 0, "[Session:{0}][BrokerResponseEnumerator] Failed to create message buffer from the response message: {1}", this.session.Id, e); clientSideException = e; } } else { // If EOM, deserialize EndOfResponses message endOfResponses = (EndOfResponses)this.endOfResponsesConverter.FromMessage(response); } string messageId = String.Empty; try { messageId = SoaHelper.GetMessageId(response).ToString(); } catch { // Swallow possible ObjectDisposedException } SessionBase.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "[Session:{0}][BrokerResponseEnumerator] Response received from broker. IsEOM = {1}, MessageId = {2}", this.session.Id, isEOM, messageId); lock (this.responsesLock) { // If the enumerator is disposing or disposed, return if (this.isDisposeCalled) { return; } // If this is not an EOM message if (!isEOM) { string actionFromResponse = BrokerClientBase.GetActionFromResponseMessage(response); // Save the response in the current window MessageWindowItem messageItem = new MessageWindowItem( messageBuffer, actionFromResponse, !response.IsFault ? response.Headers.Action : String.Empty, response.Headers.RelatesTo == null ? SoaHelper.GetMessageId(response) : response.Headers.RelatesTo); // Bug #15946: handling client side exception thrown from WebResponseHandler(when talking to rest service) // To propagate the client side exception to BrokerResponseEnumerator api, store the exception in MessageWindowItem if (string.Equals(response.Headers.Action, Constant.WebAPI_ClientSideException, StringComparison.Ordinal)) { clientSideException = response.Properties[Constant.WebAPI_ClientSideException] as Exception; } messageItem.CarriedException = clientSideException; this.currentReceiveWindow.Enqueue(messageItem); // Increment the current number of responses this.currentResponseCount++; // Lower number of outstanding requests if (Interlocked.Decrement(ref this.outstandingRequestCount) == 0) { try { this.GetMoreResponses(false, ResponseWindowSize); } catch (Exception e) { SessionBase.TraceSource.TraceEvent(TraceEventType.Error, 0, "[Session:{0}][BrokerResponseEnumerator] Failed to get more responses in SendResponse context: {1}", this.session.Id, e); // Send broker down signal since connection to broker might be bad ((IResponseServiceCallback)this).SendBrokerDownSignal(false); } } // If the current window is full or there are no more responses if (this.currentReceiveWindow.Count == ResponseWindowSize || this.currentResponseCount == this.totalResponseCount) { // Disable the flush timer this.flushResponsesTimer.Change(Timeout.Infinite, Timeout.Infinite); // Immediately flush the receive window this.FlushCurrentReceiveWindow(); } // Otherwise reset the flushtimer to the full interval else { this.flushResponsesTimer.Change(this.flushResponsesTimerInterval, Timeout.Infinite); } } else { // If we already received an EOM, ignore any others. We may get multiple EOM since // more responses are requested until we get EOM. This can lead to overrequesting. if (this.totalResponseCount != -1) { return; } // Save total response count this.totalResponseCount = endOfResponses.Count; this.endOfResponsesReason = endOfResponses.Reason; // If there are no more responses if (this.currentResponseCount == this.totalResponseCount) { // Make the current receive window available to enumerator if it has responses if (this.currentReceiveWindow.Count != 0) { this.responsesWindows.Enqueue(this.currentReceiveWindow); this.currentReceiveWindow = new Queue <MessageWindowItem>(); } // Notify enumerator that a new window is available. Notify even if // its empty since it allows MoveNext to exit and return there are no // more items this.newResponseWindowOrEOM.Set(); } } } } catch (Exception e) { // If we get an exception here log and dispose the enum this.Dispose(); SessionBase.TraceSource.TraceEvent(TraceEventType.Error, 0, "[Session:{0}][BrokerResponseEnumerator] Unhandled exception in response enumerator callback :{1}", this.session.Id, e); } }
/// <summary> /// Async Pattern. Callback of BeginProcessMessage method. /// </summary> /// <param name="state">state object</param> private void InternalBeginAddRequest(object state) { Message requestMessage = null; SessionBase.TraceSource.TraceInformation( SoaHelper.CreateTraceMessage( "MessageSender.Worker", "InternalBeginAddRequest", "Request queue length is {0}.", this.requestMessages.Count)); try { if (!this.requestMessages.TryDequeue(out requestMessage)) { SessionBase.TraceSource.TraceInformation( SoaHelper.CreateTraceMessage( "MessageSender.Worker", "InternalBeginAddRequest", "Local response cache is empty.")); } } catch (Exception e) { SessionBase.TraceSource.TraceInformation( SoaHelper.CreateTraceMessage( "MessageSender.Worker", "InternalBeginAddRequest", string.Format("Failed to get response from local cache, {0}", e))); } if (requestMessage == null) { SessionBase.TraceSource.TraceInformation( SoaHelper.CreateTraceMessage( "MessageSender.Worker", "InternalBeginAddRequest", string.Format("Get null request from local cache, trigger the time to wait"))); this.TriggerTimer(); return; } try { UniqueId messageId = SoaHelper.GetMessageId(requestMessage); if (messageId == null) { messageId = new UniqueId(); } ReliableQueueClient.ReliableState reliableState = new ReliableQueueClient.ReliableState(requestMessage, messageId); byte[] messageData; using (requestMessage) { messageData = AzureQueueMessageItem.Serialize(requestMessage); } this.waitHandler.Reset(); this.requestStorageClient.BeginAddMessage(messageData, messageId, this.AddMessageCallback, reliableState); } catch (Exception e) { this.waitHandler.Set(); SessionBase.TraceSource.TraceInformation( SoaHelper.CreateTraceMessage( "MessageSender.Worker", "InternalBeginAddRequest", "Failed to add response message, {0}", e)); this.TriggerTimer(); } }
/// <summary> /// Async Pattern. Callback of BeginProcessMessage method. /// </summary> /// <param name="state">state object</param> private void InternalBeginAddResponse(object state) { Message responseMessage = null; Tuple <ConcurrentQueue <Message>, AzureStorageClient> messageClient = null; try { foreach (Tuple <ConcurrentQueue <Message>, AzureStorageClient> responseMessageClient in this.responseMessageClients.Values) { if (!responseMessageClient.Item1.TryDequeue(out responseMessage)) { BrokerTracing.TraceInfo( SoaHelper.CreateTraceMessage( "MessageSender.Worker", "InternalBeginAddResponse", "Local response cache is empty.")); } else { messageClient = responseMessageClient; break; } } } catch (Exception e) { BrokerTracing.TraceError( SoaHelper.CreateTraceMessage( "MessageSender.Worker", "InternalBeginAddResponse", string.Format("Failed to get response from local cache, {0}", e))); } if (responseMessage == null) { this.TriggerTimer(); return; } BrokerTracing.TraceInfo( SoaHelper.CreateTraceMessage( "MessageSender.Worker", "InternalBeginAddResponse", "Retrieved message from local response cache.")); try { UniqueId messageId = SoaHelper.GetMessageId(responseMessage); if (messageId == null) { messageId = new UniqueId(); } ReliableQueueClient.ReliableState reliableState = new ReliableQueueClient.ReliableState(responseMessage, messageClient, messageId); this.waitHandler.Reset(); byte[] messageData; using (responseMessage) { messageData = AzureQueueMessageItem.Serialize(responseMessage); } messageClient.Item2.BeginAddMessage(messageData, messageId, this.AddMessageCallback, reliableState); } catch (Exception e) { this.waitHandler.Set(); BrokerTracing.TraceError( SoaHelper.CreateTraceMessage( "MessageSender.Worker", "InternalBeginAddResponse", "Failed to add response message, {0}", e)); this.TriggerTimer(); } }
/// <summary> /// Retrieve message id from a BrokerQueueItem instance /// </summary> /// <param name="messageItem">the BrokerQueueItem instance</param> /// <returns>message id of the message represented by the BrokerQueueItem instance</returns> private static string GetMessageId(BrokerQueueItem messageItem) { UniqueId messageId = SoaHelper.GetMessageId(messageItem.Message); return(messageId == null ? string.Empty : messageId.ToString()); }