0

First of all, I present context of my case:
I am using spring-boot and spring-rabbitmq. It is working for me, you should know that I must have implemented custom converter for received messages.
(1) From this converter can be thrown exception, for instance in case of inproper message and so on.
(2) After successfull converting (no exception) listener is invoked. Then, in the listener it can also be thrown exception.


Now, I would like to force two things:
(1') Don't requeue message in case of expcetion in converter. Simply, send acknowledgment to queue and simulate that everything is alright.
(2') What is default setting in this case ? When internal spring-rabbitmq engine decide to send acknowledgment to queue ? When it decide command to requeue ? Is it possible to manage it in depends on situation ?

I found in docs:

If retries are not enabled and the listener throws an exception, by default the delivery will be retried indefinitely. You can modify this behavior in two ways; set the defaultRequeueRejected property to false and zero re-deliveries will be attempted; or, throw an AmqpRejectAndDontRequeueException to signal the message should be rejected. This is the mechanism used when retries are enabled and the maximum delivery attempts are reached.

For example, in depends on catched exception in listener I should decide if I would like to requeue message, as I think (simply by throwing from catch AmqpRejectAndDontRequeueException). I am not sure if it is good way and it is why I am asking you about your opinion.

1 Answers1

0

Please read the reference manual.

The behavior is (mostly) controlled by the ErrorHandler.

Throw a MessageConversionException - the container requeues messages for most exceptions but some exceptions are considered fatal. Generally, if a message can't be converted, then redelivering it makes no sense.

This is all clearly explained in the section (surprisingly?) called Exception Handling

Starting with version 1.3.2, the default ErrorHandler is now a ConditionalRejectingErrorHandler which will reject (and not requeue) messages that fail with an irrecoverable error:

o.s.amqp...MessageConversionException

o.s.messaging...MessageConversionException

o.s.messaging...MethodArgumentNotValidException

o.s.messaging...MethodArgumentTypeMismatchException

java.lang.NoSuchMethodException

java.lang.ClassCastException

The first can be thrown when converting the incoming message payload using a MessageConverter. The second may be thrown by the conversion service if additional conversion is required when mapping to a @RabbitListener method. The third may be thrown if validation (e.g. @Valid) is used in the listener and the validation fails. The fourth may be thrown if the inbound message was converted to a type that is not correct for the target method. For example, the parameter is declared as Message but Message is received.

The fifth and sixth were added in version 1.6.3.

You can customize the ErrorHandler as needed.

Community
  • 1
  • 1
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • And nice thing that overridiny ErroHandler I can manage of sending (n)acks. Ok, thanks. Default behaviour is reasonable. requeue means pop message and push at the end of queue ? –  Mar 23 '17 at 14:48
  • No; rabbitmq requeues rejected messages at the head of the queue. – Gary Russell Mar 23 '17 at 14:50
  • So in pesimistic case we can looping. The problem is that other (correct) messages are blocked, because it is FIFO. Maybe some as `n` chances for message exists ? –  Mar 23 '17 at 14:54
  • There is a technique you can use - route the failed message to a DLQ, set a TTL on the DLQ and have it dead-letter expired messages back to the original queue. You can examine the x-death header to see how many times around the loop it has gone. See [this answer](http://stackoverflow.com/questions/31248441/requeueing-dead-letter-messages/31249346#31249346) for more information. – Gary Russell Mar 23 '17 at 15:04
  • Ok, I read some about possible solution. I can see now two approaches: set `death-x` header or DLX/DLQ approach. This second requires only `rabbitmq` configuration, no spring. In this second solution rejected messages are written into some (lets say) box and periodically requeue to original queue. Of couerse messages rejected by `MessageConverterException` are removed, not moved to DLX. I think that messages in DLX are permanently stored. However, what approach should I use ? This second require spring code and give full control on livecycle of message (`n` chances), Any link about it ? –  Mar 23 '17 at 15:49
  • No; it doesn't work that way; all rejections will go to the DLX/DLQ if configured regardless of exception type; throw `ImmediateAcknowledgeAmqpException` to completely discard messages. – Gary Russell Mar 23 '17 at 15:53
  • Ok, so type of exception can choose future for rejected message. However, what approach is ok for me ? –  Mar 23 '17 at 15:55
  • It depends on your requirements. – Gary Russell Mar 23 '17 at 15:57
  • Ok, is there some description about `x-death` headers ? Rejected messages (by proper exception) that are stored in DLX/DLQ are permanently in this place ? –  Mar 23 '17 at 16:01
  • Btw, `rabbitmq` keeps got message on the top on queue until ack ? So permanently or some timeout is defaultly set ? –  Mar 23 '17 at 16:45
  • You shouldn't keep asking new questions here; start a new one; the admins don't like long discussions. The message remains in an unack'd state until the consumer acks/nacks - it's not requeued on a timer - only if the consumer/channel/connection dies. – Gary Russell Mar 23 '17 at 16:50
  • Ok, so lat quesion: unack'd state - it means that message is on the top of queue ? –  Mar 23 '17 at 17:02
  • No; it means it has been sent to a consumer and is waiting for ack/nack. – Gary Russell Mar 23 '17 at 17:34