DLX in rabbitmq and spring-rabbitmq - some conside

2020-05-06 17:30发布

问题:

I did read this reference: https://www.rabbitmq.com/dlx.html, however it doesn't resolve my doubts, namely:
In case of accepting message there is no problem - spring-rabbitmq send ack and everthing is fine, DLX doesn't know about acked message.

The problem is in case rejecting answer, namely what about throwing MessageConverterException ? This message is removed or moved to DLX?

And what about in case other exception ? For example Exception ? It is removed/requeued/moved to DLX ?

Edit after answer of @Gary
I think, that after answer's @Gary I should add more details about my case and some summary of @Gary's answer. @Gary exactly grasped my use case.

I wouldn't like requeue - never (I am afraid of looping), but I wouldn't like to lose messages when an exception was thrown (for example lost connection to database) - this message should be resend to DLX. On the other hand, conversion of message should be treated as fatal error - no requeue, no resend to DLX - simply permanent removing message. Generally, in depends on exception either reject (=resend to DLX if configured) or accept, never requeue.

To sum up in a nutshell approach proposed by @Gary.
First: We can override ExceptionHandler to manage of sending nack/ack, which gives to us a full control.
Second: IMO simpler, solution is to set defaultRequeueRejected=false and in converter throw ImmediateAcknowledgeAmqpException. It makes that RabbitMQ think that answer was accepted (the same thing as in case of first solution), moreover listener wouldn't be invoked. **Conclusion**: UsingImmediateAcknowledgeAmqpExceptionorExceptionHandler` exception we have a full control on permanent rejecting message (under hood ack) and resending to DLX.

回答1:

RabbitMQ knows nothing about the exceptions.

When the container catches an exception it calls channel.basicReject(deliveryTag, requeue).

If requeue is true, the message is requeued.

By default, for any exception other than those mentioned here

o.s.amqp...MessageConversionException

o.s.messaging...MessageConversionException

o.s.messaging...MethodArgumentNotValidException

o.s.messaging...MethodArgumentTypeMismatchException

java.lang.NoSuchMethodException

java.lang.ClassCastException

requeue is set to true, so the message is requeued.

For those exceptions, the delivery is considered fatal and the message is NOT requeued, it will go to a DLX/DLQ if one is configured.

The container has a flag defaultRequeueRejected which is true by default; if you set it to false; no exceptions will be requeued.

For application-level exceptions, generally, messages will be requeued. To dynamically reject (and not requeue) a message, make sure there is an AmqpRejectAndDontRequeueException in the cause chain. This instructs the container to not requeue the message, and it will go to the DLX/DLQ (if configured). This behavior is enabled by the defaultRequeueRejected flag mentioned above.

This is all explained in the documentation and, as I have discussed in other answers to you, you can change this behavior by using a custom error handler; that, too, is explained in the documentation.

It is not possible to send some exceptions to the DLX/DLQ and not others; rabbit only has a binary option, requeue or don't requeue and, for the latter, if a DLX/DLQ is configured all such rejected messages go to the DLX/DLQ.

Spring AMQP provides one more exception, ImmediateAcknowledgeAmqpException. If your listener throws this exception, the message will be ack'd as if it was processed successfully (channel.basicAck()). That is the only technique provided by the container, to discard a bad message without sending it to the DLX/DLQ.

Of course, your application can drop such messsages itself.

If you want to DLX/DLQ all business exceptions but drop conversion exceptions, throw AmqpRejectAndDontRequeueException (or set defaultRequeueRejected to false), and throw ImmediateAcknowledgeAmqpException from your converter.