Example #1
0
        internal void ReturnModel(AmqpModelContainer modelContainer)
        {
            // NOTE: do not call AmqpModelContainer.Close() here.
            // That method calls this method.

#if !DISABLE_CHANNELPOOLING
            //Do not return channel to pool if either:
            //1. Pooler is disposed
            //2. Channel is consumer related
            //3. Channel has expired
            if (_disposed || modelContainer.Discard || HasModelExpired(Environment.TickCount, modelContainer))
            {
                DestroyModel(modelContainer);
                return;
            }

            //Insert model in pool
            ConcurrentQueue <AmqpModelContainer> queue = _pool[(int)modelContainer.Flags];
            queue.Enqueue(modelContainer);

            //It's possible for the disposed flag to be set (and the pool flushed) right after the first _disposed check
            //but before the modelContainer was added, so check again.
            if (_disposed)
            {
                Flush();
            }
#else
            DisposeModel(modelContainer);
#endif
        }
Example #2
0
 private static void DisposeModel(AmqpModelContainer modelContainer)
 {
     if (modelContainer != null && modelContainer.Channel != null)
     {
         try
         {
             modelContainer.Channel.Dispose();
         }
         catch { }
     }
 }
        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;

        }
        internal AmqpModelContainer GetModel(ChannelFlags flags)
        {
#if ENABLE_CHANNELPOOLING

            //Search pool for a model:
            AmqpModelContainer model = null;

            //First get the queue
            ConcurrentQueue<AmqpModelContainer> queue;
            if (_pool.TryGetValue(flags, out queue))
            {
                bool retry;
                int tick = Environment.TickCount;

                //Dequeue queue until an unexpired model is found 
                do
                {
                    retry = false;
                    model = null;
                    if (queue.TryDequeue(out model))
                    {
                        if (HasModelExpired(tick, model))
                        {

                            DisposeModel(model); // dispose model
                            retry = true;
                        }
                    }
                }
                while (retry );
            }

            if (model == null)
            {
                //Wasn't found, so create a new one
                model = new AmqpModelContainer(conn.CreateModel(), flags, this);
            }

            return model;


#else
            return new AmqpModelContainer( conn.CreateModel(), flags, this);
#endif

        }
        internal void ReturnModel(AmqpModelContainer modelContainer)
        {
            // NOTE: do not call AmqpModelContainer.Close() here.
            // That method calls this method.

#if ENABLE_CHANNELPOOLING

            if (_disposed.IsTrue || HasModelExpired(Environment.TickCount, modelContainer))
            {
                DisposeModel(modelContainer);
                return;
            }

            //Insert model in pool
            ConcurrentQueue<AmqpModelContainer> queue;
            if (_pool.TryGetValue(modelContainer.Flags, out queue))
            {
                //Found the queue so just enqueue the model
                queue.Enqueue(modelContainer);

                //TODO: AddOrUpdate below performs this lookup so this code here is redundant.
                //Consider removing this code and using an Add factory to eliminate the new queue allocation below
            }
            else
            {
                //Attempt to add a new queue, if a queue doesn't exist and if it does, then add model to queue

                queue = new ConcurrentQueue<AmqpModelContainer>();
                queue.Enqueue(modelContainer);

                _pool.AddOrUpdate(modelContainer.Flags, queue, (f, q) => { q.Enqueue(modelContainer); return q; });
            }


            //It's possible for the disposed flag to be set (and the pool flushed) right after the first _disposed check
            //but before the modelContainer was added, so check again. 
            if (_disposed.IsTrue)
            {
                Flush();
            }

#else
            DisposeModel(modelContainer);
#endif
        }
Example #7
0
        internal AmqpModelContainer GetModel(ChannelFlags flags)
        {
#if !DISABLE_CHANNELPOOLING
            if (flags == ChannelFlags.Consumer)
            {
                //Do not pool consumer related channels
                return(CreateModel(flags));
            }

            //Search pool for a model:
            AmqpModelContainer model = null;

            //First get the queue
            ConcurrentQueue <AmqpModelContainer> queue = _pool[(int)flags];

            bool retry;
            int  tick = Environment.TickCount;

            //Dequeue queue until an unexpired model is found
            do
            {
                retry = false;
                model = null;
                if (queue.TryDequeue(out model))
                {
                    if (HasModelExpired(tick, model))
                    {
                        DestroyModel(model); // dispose model for good
                        retry = true;
                    }
                }
            }while (retry);

            if (model == null)
            {
                //Wasn't found, so create a new one
                model = CreateModel(flags);
            }

            return(model);
#else
            return(CreateModel(flags));
#endif
        }
Example #8
0
        internal void ReturnModel(AmqpModelContainer modelContainer)
        {
            // NOTE: do not call AmqpModelContainer.Close() here.
            // That method calls this method.

#if ENABLE_CHANNELPOOLING
            if (_disposed.IsTrue || HasModelExpired(Environment.TickCount, modelContainer))
            {
                DisposeModel(modelContainer);
                return;
            }

            //Insert model in pool
            ConcurrentQueue <AmqpModelContainer> queue;
            if (_pool.TryGetValue(modelContainer.Flags, out queue))
            {
                //Found the queue so just enqueue the model
                queue.Enqueue(modelContainer);

                //TODO: AddOrUpdate below performs this lookup so this code here is redundant.
                //Consider removing this code and using an Add factory to eliminate the new queue allocation below
            }
            else
            {
                //Attempt to add a new queue, if a queue doesn't exist and if it does, then add model to queue

                queue = new ConcurrentQueue <AmqpModelContainer>();
                queue.Enqueue(modelContainer);

                _pool.AddOrUpdate(modelContainer.Flags, queue, (f, q) => { q.Enqueue(modelContainer); return(q); });
            }


            //It's possible for the disposed flag to be set (and the pool flushed) right after the first _disposed check
            //but before the modelContainer was added, so check again.
            if (_disposed.IsTrue)
            {
                Flush();
            }
#else
            DisposeModel(modelContainer);
#endif
        }
Example #9
0
        internal AmqpModelContainer GetModel(ChannelFlags flags)
        {
#if ENABLE_CHANNELPOOLING
            //Search pool for a model:
            AmqpModelContainer model = null;

            //First get the queue
            ConcurrentQueue <AmqpModelContainer> queue;
            if (_pool.TryGetValue(flags, out queue))
            {
                bool retry;
                int  tick = Environment.TickCount;

                //Dequeue queue until an unexpired model is found
                do
                {
                    retry = false;
                    model = null;
                    if (queue.TryDequeue(out model))
                    {
                        if (HasModelExpired(tick, model))
                        {
                            DisposeModel(model); // dispose model
                            retry = true;
                        }
                    }
                }while (retry);
            }

            if (model == null)
            {
                //Wasn't found, so create a new one
                model = new AmqpModelContainer(conn.CreateModel(), flags, this);
            }

            return(model);
#else
            return(new AmqpModelContainer(conn.CreateModel(), flags, this));
#endif
        }
Example #10
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 #11
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 #12
0
 private static void DisposeModel(AmqpModelContainer modelContainer)
 {
     if (modelContainer != null && modelContainer.Channel != null)
     {
         try
         {
             modelContainer.Channel.Dispose();
         }
         catch { }
     }
 }
Example #13
0
 private static bool HasModelExpired(int currentTickCount, AmqpModelContainer modelContainer)
 {
     //TickCount wrapped around (so timespan can't be trusted) or model has expired
     return currentTickCount < modelContainer.Created || modelContainer.Created < (currentTickCount - MODEL_EXPIRY_TIMESPAN);
 }
Example #14
0
 private void CloseAmqpModel(AmqpModelContainer model)
 {
     if (model != null)
     {
         model.Close();
     }
 }
Example #15
0
 private static bool HasModelExpired(int currentTickCount, AmqpModelContainer modelContainer)
 {
     //TickCount wrapped around (so timespan can't be trusted) or model has expired
     return(currentTickCount < modelContainer.Created || modelContainer.Created < (currentTickCount - MODEL_EXPIRY_TIMESPAN));
 }
Example #16
0
        internal void ReturnModel(AmqpModelContainer modelContainer)
        {
            // NOTE: do not call AmqpModelContainer.Close() here.
            // That method calls this method.

#if !DISABLE_CHANNELPOOLING

            //Do not return channel to pool if either:
            //1. Pooler is disposed
            //2. Channel is consumer related
            //3. Channel has expired
            if (_disposed || modelContainer.Discard || HasModelExpired(Environment.TickCount, modelContainer))
            {
                DestroyModel(modelContainer);
                return;
            }

            //Insert model in pool
            ConcurrentQueue<AmqpModelContainer> queue = _pool[(int)modelContainer.Flags];
            queue.Enqueue(modelContainer);

            //It's possible for the disposed flag to be set (and the pool flushed) right after the first _disposed check
            //but before the modelContainer was added, so check again. 
            if (_disposed)
            {
                Flush();
            }

#else
            DisposeModel(modelContainer);
#endif
        }
Example #17
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
        }