Dead Letter routing in Spring Boot based application

Question!

I've read multiple answers on the topic and it seems that configuration I have should work properly but for some reason it doesn't.

Here is the config:

@Bean Queue intakeQueue(String name) { return new Queue(name, true); }

@Bean Exchange dlx(String name) { return new DirectExchange(name); }

@Bean Queue dlq(String name) { return new Queue(name, false, false, true); }

@Bean
Binding dlb(Exchange dlx, Queue dlq, Queue reply) {
    return BindingBuilder.bind(dlq).to(dlx).with(reply.getName()).noargs();
}

@Bean
Queue replyQueue(String name, Exchange dlx) {
    Map<String, Object> args = new HashMap<>();
    args.put("x-dead-letter-exchange", dlx.getName());
    args.put("x-dead-letter-routing-key", name);
    return new Queue(name, true, false, false, args);
}

RabbitMQ UI shows that reply queue has DLX and DLK attributes.

I send messages like

this.rabbit.convertSendAndReceive(intakeQueue, obj, message -> {
    message.getMessageProperties().setPriority(10);
    return message;
});

Message handler throws AmqpRejectAndDontRequeueException immediately after it receives a message. That's done on purpose for testing only. I've started with retry advice but since it didn't produce any results I've simplified test case.

public Object handleMessage(Object obj) throws IOException {
    throw new AmqpRejectAndDontRequeueException("Testing retries!");
}

There are two issues I see right now:

  1. Message never shows up in DLQ after ARADRE has been thrown. It does if I publish to DLQ directly from handleMessage though.
  2. convertSendAndReceive doesn't receive back anything (exception maybe?) and waits until timeout happens which is 5 minutes in my case. It might be intended but for RPC-style calls it's rather weird.

Did I miss or misconfigured something?



Answers

It's not RPC in that sense; throwing an exception in the listener will not be propagated back to the sender.

You are rejecting the delivery on the intakeQueue and the message will be routed to its DLX/DLQ (if configured).

For your test case, you should be looking at that DLQ (if there is one), not the reply queue's DLQ.

RabbitMQ has no knowledge of the queue relationships. You don't show the container or RabbitTemplate configuration but, by default, direct replyTo routing is used (which uses a "special" internal queue).

You can configure the rabbit template to use a fixed reply queue, but then you have to provide a reply listener container as described in the documentation.

If the sender has timed out when the reply is received, the template throws an ARADRE so the reply will be dead-lettered in that case.

To test that, sleep in your listener for longer than the timeout, then reply; you should then see the reply go to the reply queue's DLQ.

If you want to propagate an exception to the caller, you need to return it as the handleMessage return value.

There's currently no logic to re-throw such an exception, however, you would have to do it in your code (detect that the reply is an exception and re-throw it).

Of course, the exception type would have to be Serializable.

You could also use Spring Remoting over AMQP which will take care of propagating exceptions.



This video can help you solving your question :)
By: admin