Esempio n. 1
0
 private void CloseAmqpModel(AmqpModelContainer model)
 {
     if (model != null)
     {
         model.Close();
     }
 }
Esempio n. 2
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);
            }
        }
Esempio n. 3
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
                }
            }
        }
Esempio n. 4
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
        }
Esempio n. 5
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();
                }
            }
        }
Esempio n. 6
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(); };
        }