Example #1
0
 private void CloseAmqpModel(AmqpModelContainer model)
 {
     if (model != null)
     {
         model.Close();
     }
 }
Example #2
0
        private void RedeclareExchangesAndQueues(AmqpModelContainer model, string serviceName)
        {
            //Redeclare exchanges and queues every minute if exchanges and queues are transient, or the first time client is sending a message
            TimeSpan elapsedSinceLastDeclareExchange = TimeSpan.FromMilliseconds(Environment.TickCount - lastExchangeDeclareTickCount);

            //TODO: Partition elapsedSinceLastDeclareExchange by serviceName and connection so that redeclares take place on new servicenames and connections.

            //Discovering firstDeclare by comparing lastExchangeDeclareTickCount to zero is not perfect
            //because tickcount can wrap back to zero (through the negative number range), if client is running long enough.
            //However, redeclaring exchanges and queues are a safe operation, so this is okay if it occurs more than once in persistent queues.
            bool firstDeclare = lastExchangeDeclareTickCount == 0;

            if (firstDeclare || (!messagingConfig.PersistentWorkQueuesAndExchanges && (elapsedSinceLastDeclareExchange.TotalMilliseconds < 0 || elapsedSinceLastDeclareExchange.TotalSeconds > 60)))
            {
                if (!firstDeclare)
                {
                    //All threads must attempt to declare exchanges and queues if it hasn't been previously declared
                    //(for instance, all threads were started at once)
                    //So do not swap out this value on first declare
                    lastExchangeDeclareTickCount = Environment.TickCount;
                }
                AmqpUtils.DeclareExchangeAndQueues(model.Channel, messageMapper, messagingConfig, serviceName, exchangeDeclareSync, null);
                if (firstDeclare)
                {
                    //Swap out this value after declaring on firstdeclare
                    lastExchangeDeclareTickCount = Environment.TickCount;
                }
            }
        }
Example #3
0
        /// <summary>Send an HTTP request as an asynchronous operation.</summary>
        /// <returns>Returns <see cref="T:System.Threading.Tasks.Task`1" />.The task object representing the asynchronous operation.</returns>
        /// <param name="request">The HTTP request message to send.</param>
        /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="request" /> was null.</exception>
        public override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            if (request.RequestUri == null && BaseAddress == null)
            {
                throw new InvalidOperationException("The request URI must either be set or BaseAddress must be set");
            }

            if (disposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
            hasKickStarted = true;
            PrepareMessage(request);

            //Get Request Options
            RequestOptions requestOptions    = GetRequestOptions(request);
            var            messageProperties = GetMessagingProperties(requestOptions);

            //Determine if message expects a response
            TimeSpan requestTimeout = GetRequestTimeout(requestOptions);
            //TODO: expectingResponse has a slightly different meaning in publisher confirms
            //where timespan may be longer than zero but MessageExpectsReply is false
            //in which case the timeout only applies to how long to wait for the publisher confirmation.
            bool expectingResponse = requestTimeout != TimeSpan.Zero && GetExpectsReply(request);

            //Declare messaging resources
            ExpectedResponse   arrival = null;
            AmqpModelContainer model   = null;
            bool   modelClosed         = false;
            string correlationId       = null;

            //Get channel pool and decide on RPC strategy
            var          pool        = connectionMgr.GetConnectedPool();
            IRPCStrategy rpcStrategy = pool.IsDirectReplyToCapable && !Settings.DisableDirectReplies ? directStrategy : callbackStrategy;

            try
            {
                #region Ensure CallbackQueue is started (If Using CallbackQueue Strategy)

                rpcStrategy.StartStrategy(pool, expectingResponse);

                #endregion

                #region Populate BasicProperties
                //Fill BasicProperties

                BasicProperties basicProperties = new BasicProperties();

                //Set message delivery mode -- Make message persistent if either:
                // 1. Properties.Persistent is true
                // 2. messagingConfig.PersistentMessages is true and Properties.Persistent is null
                // 3. messagingConfig.PersistentMessages is true and Properties.Persistent is true
                if (messageProperties.Persistent == true || (messagingConfig.PersistentMessages && messageProperties.Persistent != false))
                {
                    basicProperties.Persistent = true;
                }

                //Set Exchange Headers
                var exchangeHeaders = messageProperties.Headers ?? messageMapper.GetHeaders(request);
                if (exchangeHeaders != null)
                {
                    basicProperties.Headers = exchangeHeaders;
                }

                if (expectingResponse)
                {
                    //Set CorrelationId
                    correlationId = correlationIdGen.GetNextId();
                    basicProperties.CorrelationId = correlationId;

                    //Set Expiration if messageProperties doesn't override Client.Timeout, RequestOptions and MessageMapper.
                    if (!messageProperties.Expiration.HasValue && requestTimeout != System.Threading.Timeout.InfiniteTimeSpan &&
                        (messagingConfig.MessageExpires == null || messagingConfig.MessageExpires(request)))
                    {
                        if (requestTimeout.TotalMilliseconds > Int32.MaxValue)
                        {
                            basicProperties.Expiration = Int32.MaxValue.ToString();
                        }
                        else
                        {
                            basicProperties.Expiration = ((int)requestTimeout.TotalMilliseconds).ToString();
                        }
                    }
                }
                else if (!messageProperties.Expiration.HasValue && (messagingConfig.MessageExpires == null || messagingConfig.MessageExpires(request)))
                {
                    //Request has a zero timeout and the message mapper indicates it should expire and messageproperties expiration is not set:
                    //Set the expiration to zero which means RabbitMQ will only transmit if there is a consumer ready to receive it.
                    //If there is no ready consumer, RabbitMQ drops the message. See https://www.rabbitmq.com/ttl.html

                    basicProperties.Expiration = "0";
                }

                //Set expiration if set in message properties
                if (messageProperties.Expiration.HasValue)
                {
                    if (messageProperties.Expiration != System.Threading.Timeout.InfiniteTimeSpan)
                    {
                        var expiration = messageProperties.Expiration.Value.Duration();
                        if (expiration.TotalMilliseconds > Int32.MaxValue)
                        {
                            basicProperties.Expiration = Int32.MaxValue.ToString();
                        }
                        else
                        {
                            basicProperties.Expiration = ((int)expiration.TotalMilliseconds).ToString();
                        }
                    }
                    else
                    {
                        //Infinite Timespan indicates that message should never expire
                        basicProperties.ClearExpiration();
                    }
                }

                #endregion

                #region Get Ready to Send Message

                model = rpcStrategy.GetModel(pool, false);

                var serviceName =
                    (requestOptions == null || requestOptions.ServiceName == null)
                    ? (messageMapper.GetServiceName(request) ?? String.Empty).Trim()
                    : requestOptions.ServiceName.Trim();

                RedeclareExchangesAndQueues(model, serviceName);

                //TODO: Check if cancellation token was set before operation even began
                var taskSource = new TaskCompletionSource <HttpResponseMessage>();

                var exchangeKind = ExchangeKind.Direct;
                //TODO: Get ExchangeKind from CLient.Settings.ExchangeKind
                //TODO: Pull exchangeName from a concurrent dictionary that has a key of serviceName, exchangeKind
                //exchangeKind could be an index into arrays that have concurrentDictionaries.
                var exchangeName = AmqpUtils.GetExchangeName(messagingConfig, serviceName, exchangeKind);

                #endregion

                #region Start waiting for response
                //Start waiting for response if a request timeout is set.
                if (expectingResponse)
                {
                    //TODO: Better to just check if cancellationHasbeen requested instead of checking if it's None
                    if (!cancellationToken.Equals(System.Threading.CancellationToken.None))
                    {
                        //TODO: Have cancellationtokens cancel event trigger callbackHandle
                        //In fact turn this whole thing into an extension
                    }

                    arrival = rpcStrategy.PrepareForResponse(correlationId, basicProperties, model, request, requestTimeout, cancellationToken, taskSource);
                }

                #endregion

                #region Send Message
                //TODO: Implement routing to a different exchangeKind via substituting exchangeName
                //Send message
                model.Channel.BasicPublish(exchangeName,
                                           messageProperties.RoutingKey ?? messageMapper.GetRoutingKey(request, exchangeKind) ?? AmqpUtils.GetWorkQueueRoutingKey(),
                                           basicProperties,
                                           request.ToHttpRequestPacket().Serialize());

                //Close channel
                if (!expectingResponse || rpcStrategy.ReturnModelAfterSending)
                {
                    CloseAmqpModel(model);
                    modelClosed = true;
                }

                #endregion

                #region Cleanup if not expecting response
                //Exit with OK result if no request timeout was set.
                if (!expectingResponse)
                {
                    //TODO: Investigate adding a publisher confirm for zero timeout messages so we know that RabbitMQ did pick up the message before replying OK.
                    //Might add extra complexity to this class.

                    //Zero timespan means the client isn't interested in a response
                    taskSource.SetResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)
                    {
                        Content = _emptyByteArrayContent
                    });

                    rpcStrategy.CleanupMessagingResources(correlationId, arrival);
                }

                #endregion

                return(taskSource.Task);
            }
            catch (Exception ex)
            {
                //TODO: Log this

                if (model != null && !modelClosed)
                {
                    if (expectingResponse && model.Flags == ChannelFlags.RPC || model.Flags == ChannelFlags.RPCWithPublisherConfirms)
                    {
                        //Model might still be in use in waiting thread and so unsafe to be recycled
                        model.Discard = true;
                    }

                    CloseAmqpModel(model);
                }

                rpcStrategy.CleanupMessagingResources(correlationId, arrival);

                if (ex is HttpRequestException)
                {
                    throw;
                }
                else
                {
                    throw GetWrappedException("An error occurred while sending the request.", ex);
                }
            }
        }
Example #4
0
        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
                }
            }
        }
Example #5
0
        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);
        }
Example #6
0
        internal static void WaitForResponse(HttpRequestMessage request, ExpectedResponse arrival, TimeSpan requestTimeout, AmqpModelContainer model, bool closeModel, CancellationToken cancellationToken, TaskCompletionSource <HttpResponseMessage> taskSource, Action cleanup)
        {
            //Spawning a new task to wait on the MRESlim is slower than using ThreadPool.RegisterWaitForSingleObject
            //
            //TODO: Test task vs RegisterWaitForSingleObject modes in a super fast network environment with 40, 100, 200 all the way to 1000 threads to see what method has fastest throughput.

#if WAIT_FOR_RESPONSE_IN_TASK_MODE
            //Wait for response arrival event on a new task

            Task.Factory.StartNew(() =>
            {
                bool succeeded = arrival.ReceivedEvent.Wait(
                    requestTimeout.TotalMilliseconds > Int32.MaxValue ? TimeSpan.FromMilliseconds(Int32.MaxValue) : requestTimeout /* Covers InfiniteTimeSpan */,
                    cancellationToken);

                Thread.MemoryBarrier();         //Ensure non-cached versions of arrival are read

                //It failed either because it timed out or was cancelled
                //HttpClient treats both scenarios the same way.

                try
                {
                    if (closeModel)
                    {
                        model.Close();
                    }
                    SetResponseResult(request, !succeeded, arrival, taskSource);
                }
                catch
                {
                    //TODO: Log this:
                    // the code in the try block should be safe so this catch block should never be called,
                    // hoewever, this delegate is called on a separate thread and should be protected.
                }
                finally
                {
                    CleanupMessagingResources(correlationId, arrival);
                }
            }, cancellationToken);
#else
            //Wait for response arrival event on the ThreadPool:

            var localVariableInitLock = new object();

            lock (localVariableInitLock)
            {
                //TODO: Have cancellationToken signal WaitHandle so that threadpool stops waiting.

                RegisteredWaitHandle callbackHandle = null;
                callbackHandle = ThreadPool.RegisterWaitForSingleObject(arrival.ReceivedEvent.WaitHandle,
                                                                        (state, timedOut) =>
                {
                    //TODO: Investigate, this memorybarrier might be unnecessary since the thread is released from the threadpool
                    //after deserializationException and responsePacket is set.
                    Thread.MemoryBarrier();     //Ensure non-cached versions of arrival are read
                    try
                    {
                        //TODO: Check Cancelation Token when it's implemented

                        if (closeModel)
                        {
                            model.Close();
                        }
                        SetResponseResult(request, timedOut, arrival, taskSource);

                        lock (localVariableInitLock)
                        {
                            callbackHandle.Unregister(null);
                        }
                    }
                    catch
                    {
                        //TODO: Log this:
                        // the code in the try block should be safe so this catch block should never be called,
                        // hoewever, this delegate is called on a separate thread and should be protected.
                    }
                    finally
                    {
                        cleanup();
                    }
                },
                                                                        null,
                                                                        requestTimeout == System.Threading.Timeout.InfiniteTimeSpan ? System.Threading.Timeout.Infinite : (long)requestTimeout.TotalMilliseconds,
                                                                        true);
            }
#endif
        }
Example #7
0
        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);
        }
Example #8
0
        public void SendResponse(MessageContext context, HttpResponsePacket response)
        {
            if (disposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }

            var dispatch = context.Dispatch as MessageDispatch;

            if (dispatch != null)
            {
                //Ack request
                if (Settings.AckBehavior != SubscriberAckBehavior.Automatic && dispatch.Consumer.Model.IsOpen)
                {
                    dispatch.Consumer.Model.BasicAck(dispatch.Delivery.DeliveryTag, false);

                    //NOTE: The call above takes place in different threads silmultaneously
                    //In which case multiple threads will be using the same channel at the same time.
                    //It's okay in this case, because transmissions within a channel are synchronized, as seen in:
                    //https://github.com/rabbitmq/rabbitmq-dotnet-client/blob/f16c093f6409e11d9d77115038cb224eb39468ec/projects/client/RabbitMQ.Client/src/client/impl/ModelBase.cs#L459
                    //and
                    //https://github.com/rabbitmq/rabbitmq-dotnet-client/blob/f16c093f6409e11d9d77115038cb224eb39468ec/projects/client/RabbitMQ.Client/src/client/impl/SessionBase.cs#L177
                }
            }

            //Exit method if no replyToQueue was specified.
            if (String.IsNullOrEmpty(context.ReplyToQueue))
            {
                return;
            }

            if (_subscriberPool.Connection == null)
            {
                //TODO: Log this -- it technically shouldn't happen. Also translate to a HTTP Unreachable because it means StartCallbackQueueConsumer didn't create a connection
                throw new InvalidOperationException("This is Bad");
            }

            //Add/Update Subscriber-Id header
            response.Headers[Common.Shared.SUBSCRIBER_ID_HEADER] = subscriberIdHeader;

            //Send response
            var pooler = _subscriberPool;
            AmqpModelContainer model = null;

            try
            {
                model = pooler.GetModel(ChannelFlags.None);
                BasicProperties basicProperties = new BasicProperties {
                    CorrelationId = context.CorrelationId
                };

                model.Channel.BasicPublish(String.Empty,
                                           context.ReplyToQueue,
                                           basicProperties,
                                           response.Serialize());
            }
            finally
            {
                if (model != null)
                {
                    model.Close();
                }
            }
        }
Example #9
0
        public void Restart()
        {
            hasStarted.Set(true);

            //CLose connections and channels
            if (subscriberChannel != null)
            {
                if (subscriberConsumer != null)
                {
                    try
                    {
                        subscriberChannel.Channel.BasicCancel(subscriberConsumer.ConsumerTag);
                    }
                    catch { }
                }

                try
                {
                    subscriberChannel.Close();
                }
                catch
                {
                }
            }

            if (workChannel != null)
            {
                if (workConsumer != null)
                {
                    try
                    {
                        workChannel.Channel.BasicCancel(workConsumer.ConsumerTag);
                    }
                    catch { }
                }

                try
                {
                    workChannel.Close();
                }
                catch
                {
                }
            }

            if (_subscriberPool != null)
            {
                _subscriberPool.Dispose();
            }

            //NOTE: CreateConnection() can throw BrokerUnreachableException
            //That's okay because the exception needs to propagate to Reconnect() or Start()
            var conn = connectionFactory.CreateConnection();

            if (connectionBroken != null)
            {
                connectionBroken.Dispose();
            }
            connectionBroken = new CancellationTokenSource();

            if (stopWaitingOnQueue != null)
            {
                stopWaitingOnQueue.Dispose();
            }
            stopWaitingOnQueue = CancellationTokenSource.CreateLinkedTokenSource(disposedCancellationSource.Token, connectionBroken.Token);

            var pool = new AmqpChannelPooler(conn);

            _subscriberPool = pool;

            //Use pool reference henceforth.

            //Create work channel and declare exchanges and queues
            workChannel = pool.GetModel(ChannelFlags.Consumer);

            //Redeclare exchanges and queues
            AmqpUtils.DeclareExchangeAndQueues(workChannel.Channel, messageMapper, messagingConfig, serviceName, exchangeDeclareSync, Id);

            //Listen on work queue
            workConsumer = new ConcurrentQueueingConsumer(workChannel.Channel, requestQueued);
            string workQueueName = AmqpUtils.GetWorkQueueName(messagingConfig, serviceName);

            workChannel.Channel.BasicQos(0, (ushort)Settings.PrefetchCount, false);
            workChannel.Channel.BasicConsume(workQueueName, Settings.AckBehavior == SubscriberAckBehavior.Automatic, workConsumer);

            //Listen on subscriber queue
            subscriberChannel  = pool.GetModel(ChannelFlags.Consumer);
            subscriberConsumer = new ConcurrentQueueingConsumer(subscriberChannel.Channel, requestQueued);
            string subscriberWorkQueueName = AmqpUtils.GetSubscriberQueueName(serviceName, Id);

            subscriberChannel.Channel.BasicQos(0, (ushort)Settings.PrefetchCount, false);
            subscriberChannel.Channel.BasicConsume(subscriberWorkQueueName, Settings.AckBehavior == SubscriberAckBehavior.Automatic, subscriberConsumer);

            //Cancel connectionBroken on connection/consumer problems
            pool.Connection.ConnectionShutdown   += (s, e) => { connectionBroken.Cancel(); };
            workConsumer.ConsumerCancelled       += (s, e) => { connectionBroken.Cancel(); };
            subscriberConsumer.ConsumerCancelled += (s, e) => { connectionBroken.Cancel(); };
        }
Example #10
0
        internal static void WaitForResponse(HttpRequestMessage request, ExpectedResponse arrival, TimeSpan requestTimeout, AmqpModelContainer model, bool closeModel, CancellationToken cancellationToken, TaskCompletionSource <HttpResponseMessage> taskSource, Action cleanup)
        {
            var localVariableInitLock = new object();

            lock (localVariableInitLock)
            {
                //TODO: Have cancellationToken signal WaitHandle so that threadpool stops waiting.

                RegisteredWaitHandle callbackHandle = null;
                callbackHandle = ThreadPool.RegisterWaitForSingleObject(arrival.ReceivedEvent.WaitHandle,
                                                                        (state, timedOut) =>
                {
                    //TODO: Investigate, this memorybarrier might be unnecessary since the thread is released from the threadpool
                    //after deserializationException and responsePacket is set.
                    Interlocked.MemoryBarrier();     //Ensure non-cached versions of arrival are read
                    try
                    {
                        //TODO: Check Cancelation Token when it's implemented

                        if (closeModel)
                        {
                            model.Close();
                        }
                        SetResponseResult(request, timedOut, arrival, taskSource);

                        lock (localVariableInitLock)
                        {
                            callbackHandle.Unregister(null);
                        }
                    }
                    catch
                    {
                        //TODO: Log this:
                        // the code in the try block should be safe so this catch block should never be called,
                        // hoewever, this delegate is called on a separate thread and should be protected.
                    }
                    finally
                    {
                        cleanup();
                    }
                },
                                                                        null,
                                                                        requestTimeout == System.Threading.Timeout.InfiniteTimeSpan ? System.Threading.Timeout.Infinite : (long)requestTimeout.TotalMilliseconds,
                                                                        true);
            }
        }