Пример #1
0
        public void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey,
                                       IBasicProperties properties, byte[] body)
        {
            logger.DebugWrite("HandleBasicDeliver on consumer: {0}, deliveryTag: {1}", consumerTag, deliveryTag);

            if (disposed)
            {
                // this message's consumer has stopped, so just return
                logger.InfoWrite("Consumer has stopped running. Consumer '{0}' on queue '{1}'. Ignoring message",
                                 ConsumerTag, Queue.Name);
                return;
            }

            if (OnMessage == null)
            {
                logger.ErrorWrite("User consumer callback, 'onMessage' has not been set for consumer '{0}'." +
                                  "Please call InternalConsumer.StartConsuming before passing the consumer to basic.consume",
                                  ConsumerTag);
                return;
            }

            var messageReceivedInfo = new MessageReceivedInfo(consumerTag, deliveryTag, redelivered, exchange, routingKey, Queue.Name);
            var messsageProperties  = new MessageProperties(properties);
            var context             = new ConsumerExecutionContext(OnMessage, messageReceivedInfo, messsageProperties, body, this);

            consumerDispatcher.QueueAction(() =>
            {
                eventBus.Publish(new DeliveredMessageEvent(messageReceivedInfo, messsageProperties, body));
                handlerRunner.InvokeUserMessageHandler(context);
            });
        }
Пример #2
0
        public virtual void InvokeUserMessageHandler(ConsumerExecutionContext context)
        {
            Preconditions.CheckNotNull(context, "context");

            logger.DebugWrite("Received \n\tRoutingKey: '{0}'\n\tCorrelationId: '{1}'\n\tConsumerTag: '{2}'" +
                              "\n\tDeliveryTag: {3}\n\tRedelivered: {4}",
                              context.Info.RoutingKey,
                              context.Properties.CorrelationId,
                              context.Info.ConsumerTag,
                              context.Info.DeliverTag,
                              context.Info.Redelivered);

            Task completionTask;

            try
            {
                completionTask = context.UserHandler(context.Body, context.Properties, context.Info);
            }
            catch (Exception exception)
            {
                completionTask = TaskHelpers.FromException(exception);
            }

            if (completionTask.Status == TaskStatus.Created)
            {
                logger.ErrorWrite("Task returned from consumer callback is not started. ConsumerTag: '{0}'",
                                  context.Info.ConsumerTag);
                return;
            }

            completionTask.ContinueWith(task => DoAck(context, GetAckStrategy(context, task)));
        }
Пример #3
0
        protected virtual string BuildErrorMessage(ConsumerExecutionContext context, Exception exception)
        {
            var message = Encoding.UTF8.GetString(context.Body);

            return("Exception thrown by subscription callback.\n" +
                   string.Format("\tExchange:    '{0}'\n", context.Info.Exchange) +
                   string.Format("\tRouting Key: '{0}'\n", context.Info.RoutingKey) +
                   string.Format("\tRedelivered: '{0}'\n", context.Info.Redelivered) +
                   string.Format("Message:\n{0}\n", message) +
                   string.Format("BasicProperties:\n{0}\n", context.Properties) +
                   string.Format("Exception:\n{0}\n", exception));
        }
Пример #4
0
        private string DeclareErrorExchangeAndBindToDefaultErrorQueue(IModel model, ConsumerExecutionContext context)
        {
            var originalRoutingKey = context.Info.RoutingKey;

            return(errorExchanges.GetOrAdd(originalRoutingKey, _ =>
            {
                var exchangeName = conventions.ErrorExchangeNamingConvention(context.Info);
                model.ExchangeDeclare(exchangeName, ExchangeType.Direct, durable: true);
                model.QueueBind(conventions.ErrorQueueNamingConvention(), exchangeName, originalRoutingKey);
                return exchangeName;
            }));
        }
Пример #5
0
        public virtual AckStrategy HandleConsumerError(ConsumerExecutionContext context, Exception exception)
        {
            Preconditions.CheckNotNull(context, "context");
            Preconditions.CheckNotNull(exception, "exception");

            if (disposed || disposing)
            {
                logger.ErrorWrite(
                    "Consumer Error Handler: DefaultConsumerErrorStrategy was already disposed, when attempting to handle consumer error.  This can occur when messaging is being shut down through disposal of the IBus.  Message will not be ackd to RabbitMQ server and will remain on the RabbitMQ queue.  Error message will not be published to error queue.\n" +
                    "ConsumerTag: {0}, DeliveryTag: {1}\n",
                    context.Info.ConsumerTag,
                    context.Info.DeliverTag);
                return(AckStrategies.NackWithRequeue);
            }

            try
            {
                Connect();

                using (var model = connection.CreateModel())
                {
                    var errorExchange = DeclareErrorExchangeQueueStructure(model, context);

                    var messageBody = CreateErrorMessage(context, exception);
                    var properties  = model.CreateBasicProperties();
                    properties.Persistent = true;
                    properties.Type       = typeNameSerializer.Serialize(typeof(Error));

                    model.BasicPublish(errorExchange, context.Info.RoutingKey, properties, messageBody);
                }
            }
            catch (BrokerUnreachableException)
            {
                // thrown if the broker is unreachable during initial creation.
                logger.ErrorWrite("Consumer Error Handler cannot connect to Broker\n" +
                                  CreateConnectionCheckMessage());
            }
            catch (OperationInterruptedException interruptedException)
            {
                // thrown if the broker connection is broken during declare or publish.
                logger.ErrorWrite("Consumer Error Handler: Broker connection was closed while attempting to publish Error message.\n" +
                                  string.Format("Exception was: '{0}'\n", interruptedException.Message) +
                                  CreateConnectionCheckMessage());
            }
            catch (Exception unexpectedException)
            {
                // Something else unexpected has gone wrong :(
                logger.ErrorWrite("Consumer Error Handler: Failed to publish error message\nException is:\n"
                                  + unexpectedException);
            }
            return(AckStrategies.Ack);
        }
Пример #6
0
        protected virtual void DoAck(ConsumerExecutionContext context, AckStrategy ackStrategy)
        {
            const string failedToAckMessage =
                "Basic ack failed because channel was closed with message '{0}'." +
                " Message remains on RabbitMQ and will be retried." +
                " ConsumerTag: {1}, DeliveryTag: {2}";

            var ackResult = AckResult.Exception;

            try
            {
                Preconditions.CheckNotNull(context.Consumer.Model, "context.Consumer.Model");

                ackResult = ackStrategy(context.Consumer.Model, context.Info.DeliverTag);
            }
            catch (AlreadyClosedException alreadyClosedException)
            {
                logger.InfoWrite(failedToAckMessage,
                                 alreadyClosedException.Message,
                                 context.Info.ConsumerTag,
                                 context.Info.DeliverTag);
            }
            catch (IOException ioException)
            {
                logger.InfoWrite(failedToAckMessage,
                                 ioException.Message,
                                 context.Info.ConsumerTag,
                                 context.Info.DeliverTag);
            }
            catch (Exception exception)
            {
                logger.ErrorWrite("Unexpected exception when attempting to ACK or NACK\n{0}", exception);
            }
            finally
            {
                eventBus.Publish(new AckEvent(context.Info, context.Properties, context.Body, ackResult));
            }
        }
Пример #7
0
        protected virtual AckStrategy GetAckStrategy(ConsumerExecutionContext context, Task task)
        {
            var ackStrategy = AckStrategies.Ack;

            try
            {
                if (task.IsFaulted)
                {
                    logger.ErrorWrite(BuildErrorMessage(context, task.Exception));
                    ackStrategy = consumerErrorStrategy.HandleConsumerError(context, task.Exception);
                }
                else if (task.IsCanceled)
                {
                    ackStrategy = consumerErrorStrategy.HandleConsumerCancelled(context);
                }
            }
            catch (Exception consumerErrorStrategyError)
            {
                logger.ErrorWrite("Exception in ConsumerErrorStrategy:\n{0}",
                                  consumerErrorStrategyError);
                ackStrategy = AckStrategies.Nothing;
            }
            return(ackStrategy);
        }
Пример #8
0
        private byte[] CreateErrorMessage(ConsumerExecutionContext context, Exception exception)
        {
            var messageAsString = errorMessageSerializer.Serialize(context.Body);
            var error           = new Error
            {
                RoutingKey = context.Info.RoutingKey,
                Exchange   = context.Info.Exchange,
                Queue      = context.Info.Queue,
                Exception  = exception.ToString(),
                Message    = messageAsString,
                DateTime   = DateTime.UtcNow
            };

            if (context.Properties.Headers == null)
            {
                error.BasicProperties = context.Properties;
            }
            else
            {
                // we'll need to clone context.Properties as we are mutating the headers dictionary
                error.BasicProperties = (MessageProperties)context.Properties.Clone();

                // the RabbitMQClient implictly converts strings to byte[] on sending, but reads them back as byte[]
                // we're making the assumption here that any byte[] values in the headers are strings
                // and all others are basic types. RabbitMq client generally throws a nasty exception if you try
                // to store anything other than basic types in headers anyway.

                //see http://hg.rabbitmq.com/rabbitmq-dotnet-client/file/tip/projects/client/RabbitMQ.Client/src/client/impl/WireFormatting.cs

                error.BasicProperties.Headers = context.Properties.Headers.ToDictionary(
                    kvp => kvp.Key,
                    kvp => kvp.Value is byte[] ? Encoding.UTF8.GetString((byte[])kvp.Value) : kvp.Value);
            }

            return(serializer.MessageToBytes(error));
        }
Пример #9
0
 public AckStrategy HandleConsumerCancelled(ConsumerExecutionContext context)
 {
     return(AckStrategies.Ack);
 }
Пример #10
0
 private string DeclareErrorExchangeQueueStructure(IModel model, ConsumerExecutionContext context)
 {
     DeclareDefaultErrorQueue(model);
     return(DeclareErrorExchangeAndBindToDefaultErrorQueue(model, context));
 }