Пример #1
0
 internal AmqpModelContainer(IModel channel, ChannelFlags flags, AmqpChannelPooler source)
 {
     Channel = channel;
     Flags   = flags;
     Source  = source;
     Created = Environment.TickCount;
 }
Пример #2
0
 internal AmqpModelContainer(IModel channel, ChannelFlags flags, AmqpChannelPooler source)
 {
     Channel = channel;
     Flags = flags;
     Source = source;
     Created = Environment.TickCount;
 }
Пример #3
0
        public void StartStrategy(AmqpChannelPooler pool, bool requestExpectsResponse)
        {
            if (requestExpectsResponse) seenRequestExpectingResponse = true;

            if (seenRequestExpectingResponse)
            {
                //This client has seen a request expecting a response so
                //Start Callback consumer if it hasn't started
                StartCallbackQueueConsumer(pool);
            }
        }
Пример #4
0
        internal RPCModelContainer(IModel channel,
                                   bool streamsPublisherConfirms,
                                   AmqpChannelPooler source) : base(channel, streamsPublisherConfirms ? ChannelFlags.RPCWithPublisherConfirms : ChannelFlags.RPC, source)
        {
            this._receivedResponse = new ManualResetEventSlim();
            _consumer = new EventingBasicConsumer(channel);
            _consumer.ConsumerCancelled += (s, e) => SetModelToBeDiscarded();
            _consumer.Received          += ResponseReceived;

            //NOTE: Consumer is expecting just one response, so there is no need to call BasicQos here.

            //Start consuming
            channel.BasicConsume(RPCStrategyHelpers.DIRECT_REPLY_TO_QUEUENAME_ARG, true, _consumer);
        }
Пример #5
0
        internal RPCModelContainer(IModel channel,  
            bool streamsPublisherConfirms, 
            AmqpChannelPooler source ) : base(channel, streamsPublisherConfirms ? ChannelFlags.RPCWithPublisherConfirms : ChannelFlags.RPC, source )
        {
            this._receivedResponse = new ManualResetEventSlim();
            _consumer = new EventingBasicConsumer(channel);
            _consumer.ConsumerCancelled += (s, e) => SetModelToBeDiscarded();
            _consumer.Received += ResponseReceived;

            //NOTE: Consumer is expecting just one response, so there is no need to call BasicQos here.

            //Start consuming
            channel.BasicConsume(RPCStrategyHelpers.DIRECT_REPLY_TO_QUEUENAME_ARG, true, _consumer);
        }
Пример #6
0
        private AmqpChannelPooler CreateNewChannelPool()
        {
            //NOTE: This is the only place where connections are created in the client
            //NOTE: CreateConnection() can always throw RabbitMQ.Client.Exceptions.BrokerUnreachableException
            var newConn = connectionFactory.CreateConnection();

            //Swap out client connection and pooler, so other threads can use the new objects:

            //swap out old pool with new pool
            var newPool = new AmqpChannelPooler(newConn);
            var oldpool = Interlocked.Exchange(ref _clientPool, newPool);

            //Dispose old pool
            if (oldpool != null)
            {
                oldpool.Dispose();
            }

            return newPool;
        }
Пример #7
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(); };
        }
Пример #8
0
 public AmqpModelContainer GetModel(AmqpChannelPooler pool, bool streamsPublisherConfirms)
 {
     return pool.GetModel(streamsPublisherConfirms ? ChannelFlags.PublisherConfirms : ChannelFlags.None);
 }
Пример #9
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

                }
            }

        }
Пример #10
0
 public AmqpModelContainer GetModel(AmqpChannelPooler pool, bool streamsPublisherConfirms)
 {
     var model = (RPCModelContainer)pool.GetModel(streamsPublisherConfirms ? ChannelFlags.RPCWithPublisherConfirms : ChannelFlags.RPC );
     model.Reset();
     return model;
 }
Пример #11
0
 public void StartStrategy(AmqpChannelPooler pool, bool requestExpectsResponse)
 {
     //Nothing to start
 }