public ExpectedResponse PrepareForResponse(string correlationId, BasicProperties basicProperties, AmqpModelContainer model, HttpRequestMessage request, TimeSpan requestTimeout, CancellationToken cancellationToken, TaskCompletionSource <HttpResponseMessage> taskSource) { //Set Reply to queue basicProperties.ReplyTo = RPCStrategyHelpers.DIRECT_REPLY_TO_QUEUENAME_ARG; var rpcModel = (RPCModelContainer)model; var arrival = new ExpectedResponse(rpcModel.ReceivedResponseEvent); rpcModel.ExpectResponse(correlationId, arrival); RPCStrategyHelpers.WaitForResponse(request, arrival, requestTimeout, model, true, cancellationToken, taskSource, () => CleanupMessagingResources(correlationId, arrival)); return(arrival); }
public ExpectedResponse PrepareForResponse(string correlationId, BasicProperties basicProperties, AmqpModelContainer model, HttpRequestMessage request, TimeSpan requestTimeout, CancellationToken cancellationToken, TaskCompletionSource <HttpResponseMessage> taskSource) { //Set Reply to queue basicProperties.ReplyTo = callbackQueueName; //Initialize response arrival object and add to expected responses dictionary var arrival = new ExpectedResponse(); expectedResponses[correlationId] = arrival; RPCStrategyHelpers.WaitForResponse(request, arrival, requestTimeout, model, false, cancellationToken, taskSource, () => CleanupMessagingResources(correlationId, arrival)); return(arrival); }
private void StartCallbackQueueConsumer(AmqpChannelPooler pool) { //TODO: Double-checked locking -- make this better if (callbackConsumer == null || !isInConsumerLoop || !pool.Connection.IsOpen) { lock (restartConsumerSync) { if (!(callbackConsumer == null || !isInConsumerLoop || !pool.Connection.IsOpen)) { return; } //This method waits on this signal to make sure the callbackprocessor thread either started successfully or failed. ManualResetEventSlim consumerSignal = new ManualResetEventSlim(false); Exception consumerSignalException = null; Thread callBackProcessor = new Thread(p => { ConcurrentQueueingConsumer consumer = null; try { //Start consumer AmqpModelContainer channelContainer = null; try { channelContainer = pool.GetModel(ChannelFlags.Consumer); IModel channel = channelContainer.Channel; if (clientSettings.DisableDirectReplies || !channelContainer.IsDirectReplyToCapable) { DeclareIndirectReplyToQueue(channel, indirectReplyToQueueName); } consumer = new ConcurrentQueueingConsumer(channel, responseQueued); //Set consumerCancelled to true on consumer cancellation consumerCancelled = false; consumer.ConsumerCancelled += (s, e) => { consumerCancelled = true; }; channel.BasicQos(0, (ushort)clientSettings.PrefetchCount, false); //Start consumer: string replyToQueueName; if (clientSettings.DisableDirectReplies || !channelContainer.IsDirectReplyToCapable) { channel.BasicConsume(indirectReplyToQueueName, clientSettings.AckBehavior == ClientAckBehavior.Automatic, consumer); replyToQueueName = indirectReplyToQueueName; } else { //TODO: REMOVE LATER: This clause is never called because in this case the DirectReplyToRPCStrategy would be in use //instead of this strategy. //throw an InvalidOperationException instead channel.BasicConsume(RPCStrategyHelpers.DIRECT_REPLY_TO_QUEUENAME_ARG, true, consumer); //Discover direct reply to queue name replyToQueueName = DiscoverDirectReplyToQueueName(channel, indirectReplyToQueueName); } //Set callbackConsumer to consumer callbackQueueName = replyToQueueName; callbackConsumer = consumer; //Notify outer thread that channel has started consumption consumerSignal.Set(); BasicDeliverEventArgs evt; ExpectedResponse expected; isInConsumerLoop = true; while (true) { try { evt = DequeueCallbackQueue(); } catch { //TODO: Log this exception except it's ObjectDisposedException or OperationCancelledException throw; } expected = null; if (!String.IsNullOrEmpty(evt.BasicProperties.CorrelationId) && expectedResponses.TryRemove(evt.BasicProperties.CorrelationId, out expected)) { RPCStrategyHelpers.ReadAndSignalDelivery(expected, evt); } //Acknowledge receipt: //In ClientBehavior.Automatic mode //Client acks all received messages, even if it wasn't the expected one or even if it wasn't expecting anything. //This prevents a situation where crap messages are sent to the client but the good expected message is stuck behind the //crap ones and isn't delivered because the crap ones in front of the queue aren't acked and crap messages exceed prefetchCount. //In ClientAckBehavior.ValidResponses mode (and Direct Reply to is not in effect): //Client only acks expected messages if they could be deserialized //If not, they are rejected. if ((clientSettings.DisableDirectReplies || !channelContainer.IsDirectReplyToCapable) && clientSettings.AckBehavior == ClientAckBehavior.ValidResponses) { if (expected != null && expected.DeserializationException != null) { channel.BasicAck(evt.DeliveryTag, false); } else { channel.BasicReject(evt.DeliveryTag, false); } } //Exit loop if consumer is cancelled. if (consumerCancelled) { break; } } } finally { isInConsumerLoop = false; pool.SetRecycle(); if (channelContainer != null) { if (consumer != null && !consumerCancelled) { try { channelContainer.Channel.BasicCancel(consumer.ConsumerTag); } catch { } } channelContainer.Close(); } } } catch (Exception ex) { //TODO: Log error (Except it's object disposed exception) //Set Exception object which will be throw by signal waiter consumerSignalException = ex; //Notify outer thread to move on, in case it's still waiting try { consumerSignal.Set(); } catch { } } finally { if (pool != null) { pool.Dispose(); } } }); //Start Thread callBackProcessor.Name = "RestBus RabbitMQ Client Callback Queue Consumer"; callBackProcessor.IsBackground = true; callBackProcessor.Start(); //Wait for Thread to start consuming messages consumerSignal.Wait(); consumerSignal.Dispose(); //Examine exception if it were set and rethrow it Thread.MemoryBarrier(); //Ensure we have the non-cached version of consumerSignalException if (consumerSignalException != null) { throw consumerSignalException; } //No more code from this point in this method } } }