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); }); }
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))); }
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)); }
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; })); }
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); }
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)); } }
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); }
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)); }
public AckStrategy HandleConsumerCancelled(ConsumerExecutionContext context) { return(AckStrategies.Ack); }
private string DeclareErrorExchangeQueueStructure(IModel model, ConsumerExecutionContext context) { DeclareDefaultErrorQueue(model); return(DeclareErrorExchangeAndBindToDefaultErrorQueue(model, context)); }