예제 #1
0
        public void TestVerifyPopulatedChecks()
        {
            var producerOptions = new ProducerOptions();

            Assert.False(producerOptions.VerifyPopulated());

            producerOptions.ExchangeName = "";
            Assert.False(producerOptions.VerifyPopulated());

            producerOptions.ExchangeName = "Test.ExchangeName";
            Assert.True(producerOptions.VerifyPopulated());

            var consumerOptions = new ConsumerOptions();

            Assert.False(consumerOptions.VerifyPopulated());

            consumerOptions.QueueName = "Test.QueueName";
            Assert.False(consumerOptions.VerifyPopulated());

            consumerOptions.QoSPrefetchCount = 1234;
            Assert.True(consumerOptions.VerifyPopulated());
        }
예제 #2
0
        /// <summary>
        /// Setup a subscription to a queue which sends messages to the <see cref="IConsumer"/>.
        /// </summary>
        /// <param name="consumerOptions">The connection options.</param>
        /// <param name="consumer">Consumer that will be sent any received messages.</param>
        /// <param name="isSolo">If specified, will ensure that it is the only consumer on the provided queue</param>
        /// <returns>Identifier for the consumer task, can be used to stop the consumer without shutting down the whole adapter</returns>
        public Guid StartConsumer(ConsumerOptions consumerOptions, IConsumer consumer, bool isSolo = false)
        {
            if (ShutdownCalled)
            {
                throw new ApplicationException("Adapter has been shut down");
            }

            if (consumerOptions == null)
            {
                throw new ArgumentNullException(nameof(consumerOptions));
            }

            if (!consumerOptions.VerifyPopulated())
            {
                throw new ArgumentException("The given ConsumerOptions has invalid values");
            }

            // Client label is the same for the IConnection and Subscription since we have a separate connection per consumer
            string label = string.Format("{0}::Consumer::{1}", _hostId, consumerOptions.QueueName);

            IConnection connection = _factory.CreateConnection(label);

            connection.ConnectionBlocked   += (s, a) => _logger.Warn($"ConnectionBlocked for {consumerOptions.QueueName} ( Reason: {a.Reason})");
            connection.ConnectionUnblocked += (s, a) => _logger.Warn($"ConnectionUnblocked for {consumerOptions.QueueName}");

            IModel model = connection.CreateModel();

            model.BasicQos(0, consumerOptions.QoSPrefetchCount, false);

            // Check queue exists

            try
            {
                // Passively declare the queue (equivalent to checking the queue exists)
                model.QueueDeclarePassive(consumerOptions.QueueName);
            }
            catch (OperationInterruptedException e)
            {
                model.Close(200, "StartConsumer - Queue missing");
                connection.Close(200, "StartConsumer - Queue missing");

                throw new ApplicationException($"Expected queue \"{consumerOptions.QueueName}\" to exist", e);
            }

            if (isSolo && model.ConsumerCount(consumerOptions.QueueName) > 0)
            {
                model.Close(200, "StartConsumer - Already a consumer on the queue");
                connection.Close(200, "StartConsumer - Already a consumer on the queue");

                throw new ApplicationException($"Already a consumer on queue {consumerOptions.QueueName} and solo consumer was specified");
            }

            Subscription subscription = null;
            var          connected    = false;
            var          failed       = 0;

            while (!connected)
            {
                try
                {
                    subscription = new Subscription(model, consumerOptions.QueueName, consumerOptions.AutoAck, label);
                    connected    = true;
                }
                catch (TimeoutException)
                {
                    if (++failed >= MaxSubscriptionAttempts)
                    {
                        _logger.Warn("Retries exceeded, throwing exception");
                        throw;
                    }

                    _logger.Warn($"Timeout when creating Subscription, retrying in 5s...");
                    Thread.Sleep(TimeSpan.FromSeconds(5));
                }
                catch (OperationInterruptedException e)
                {
                    throw new ApplicationException(
                              $"Error when creating subscription on queue \"{consumerOptions.QueueName}\"", e);
                }
                finally
                {
                    if (!connected)
                    {
                        model.Close(200, "StartConsumer - Couldn't create subscription");
                        connection.Close(200, "StartConsumer - Couldn't create subscription");
                    }
                }
            }

            Guid taskId          = Guid.NewGuid();
            var  taskTokenSource = new CancellationTokenSource();

            var consumerTask = new Task(() => Consume(subscription, consumer, taskTokenSource.Token));

            var resources = new ConsumerResources
            {
                Connection   = connection,
                Model        = model,
                Subscription = subscription,
                ConsumerTask = consumerTask,
                TokenSource  = taskTokenSource
            };

            lock (_oResourceLock)
            {
                _rabbitResources.Add(taskId, resources);
            }

            consumer.OnFatal += (s, e) =>
            {
                resources.Shutdown(DefaultOperationTimeout);
                _hostFatalHandler(s, e);
            };

            consumerTask.Start();
            _logger.Debug($"Consumer task started [QueueName={subscription?.QueueName}]");

            return(taskId);
        }