Dead letter exchanges, TTL and retry with backoff
A dead letter exchange plus a TTL queue is how you build delayed retry without any plugin.
Open this lesson in the learning hubKey points
- Set
x-dead-letter-exchangeon a queue and rejected messages are republished there. - Exactly four reasons dead letter a message: rejected, expired, maxlen and delivery_limit.
- The
x-deathheader records the reason, the origin queue, an attempt count and a timestamp. - Queue-wide
x-message-ttland the per-messageexpirationproperty are both in milliseconds. - A per-message TTL only fires at the head of the queue, so one slow message delays the ones behind it.
- Backoff is just a wait queue with no consumer: the TTL expires and its DLX returns the message.
Example
// work queue -> on reject, goes to the retry exchange
Map<String, Object> work = Map.of(
"x-dead-letter-exchange", "orders.retry");
ch.queueDeclare("invoicing", true, false, false, work);
// retry queue has NO consumer - the TTL is the delay
Map<String, Object> retry = Map.of(
"x-message-ttl", 30_000, // wait 30s
"x-dead-letter-exchange", "orders", // then back to work
"x-dead-letter-routing-key", "order.created");
ch.queueDeclare("invoicing.retry.30s", true, false, false, retry);
ch.queueBind("invoicing.retry.30s", "orders.retry", "order.created");
// chain 5s / 30s / 5m queues for exponential backoff, and give the
// last one a DLX pointing at a parking lot queue nobody drains
A retry queue is a queue with no consumer: the TTL is the delay and the DLX is the return path.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the RabbitMQ Course course, and every lesson in it is listed on the RabbitMQ Course contents page.