Cheat SheetsRabbitMQDead-Lettering

Dead-Lettering — Cheat Sheet

RabbitMQ · 4 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
Dead-Lettering
RabbitMQ4 topicsQuick revision reference
1

Dead Letter Exchange (DLX)

Rejected, nacked, or expired messages are routed to a DLX; configure x-dead-letter-exchange on the source queue. DLQs are essential for failure inspection and retry workflows.

  • A DLX is a regular exchange; configure it on the source queue via x-dead-letter-exchange argument.
  • Messages dead-letter for three reasons: nacked with requeue=false, TTL expired, or queue overflow (reject-publish policy).
  • Always monitor DLQ depth — a growing DLQ indicates a broken consumer or a schema/data problem.
  • The x-death header tracks how many times a message has been dead-lettered and the reason — use it for retry-count logic.
  • Retry-with-backoff pattern: nack → retry queue with TTL → expires back to source queue → after N retries → permanent DLQ.
  • In Spring AMQP, use QueueBuilder.withArgument("x-dead-letter-exchange", "dlx-name") to configure the DLX on the source queue.
Java — DLX Setup (AMQP Client)
Channel channel = connection.createChannel();

// 1. Declare the DLX (a regular direct exchange)
channel.exchangeDeclare("orders.dlx", BuiltinExchangeType.DIRECT, true);

// 2. Declare the DLQ
channel.queueDeclare("orders.dlq", true, false, false, null);

// 3. Bind DLQ to DLX
channel.queueBind("orders.dlq", "orders.dlx", "order-queue");

// 4. Declare the source queue with DLX argument
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange",    "orders.dlx");
args.put("x-dead-letter-routing-key", "order-queue"); // optional override
args.put("x-message-ttl",             60_000);        // 60 s TTL — expired msgs → DLX

channel.queueDeclare("order-queue", true, false, false, args);

// Now: nack with requeue=false → message goes to orders.dlq
// Or: message not consumed within 60s → goes to orders.dlq
2

Dead Letter Queues (DLQ)

Messages that cannot be processed land in a DLQ; monitor DLQ depth as an SLA signal and build tooling to replay, inspect, or discard messages after investigation.

  • Messages are dead-lettered on: basicNack(requeue=false), TTL expiry, or queue length overflow
  • DLX + DLQ must be explicitly configured — set x-dead-letter-exchange on the source queue
  • x-death header on dead-lettered messages contains reason, count, and origin queue
  • DLQ depth is a critical SLA metric — alert immediately when DLQ becomes non-empty
  • RejectAndDontRequeueRecoverer + Spring Retry auto-routes to DLQ after exhausting retries
  • Build replay tooling: re-publish DLQ messages to the original exchange for re-processing
Spring AMQP — DLX and DLQ configuration
@Configuration
class DlqConfig {

    // DLX — a direct exchange that routes to the DLQ
    @Bean
    DirectExchange deadLetterExchange() {
        return new DirectExchange("orders.dlx");
    }

    // DLQ — the queue that holds dead-lettered messages
    @Bean
    Queue deadLetterQueue() {
        return QueueBuilder.durable("orders.dlq").build();
    }

    @Bean
    Binding dlqBinding() {
        return BindingBuilder.bind(deadLetterQueue())
            .to(deadLetterExchange())
            .with("orders.dead");   // routing key
    }

    // Source queue — messages here get dead-lettered to DLX
    @Bean
    Queue ordersQueue() {
        return QueueBuilder.durable("orders.processing")
            .withArgument("x-dead-letter-exchange",    "orders.dlx")
            .withArgument("x-dead-letter-routing-key", "orders.dead")
            .withArgument("x-message-ttl",             300_000)   // 5min TTL
            .build();
    }
}
3

Message TTL

Set x-message-ttl (queue-level) or the expiration property (per-message) to expire messages not consumed within the specified milliseconds; expired messages are dead-lettered.

  • x-message-ttl (queue declaration) applies a uniform TTL to all messages in that queue
  • expiration (per-message property, set by producer) allows different TTLs per message
  • Queue-level TTL only expires messages at the queue HEAD — not mid-queue
  • Expired messages are dead-lettered to x-dead-letter-exchange or silently dropped
  • TTL + DLX enables retry-with-delay: wait queues with TTL automatically re-route after expiry
  • For exponential backoff: use multiple wait queues (10s, 30s, 60s) and route based on x-death count
Spring AMQP — queue-level x-message-ttl
@Bean
Queue ordersQueue() {
    return QueueBuilder.durable("orders.processing")
        .withArgument("x-message-ttl", 60_000)             // 60 seconds
        .withArgument("x-dead-letter-exchange", "orders.dlx")
        .build();
}

// Or via RabbitAdmin (runtime)
rabbitAdmin.declareQueue(new Queue("orders.processing", true, false, false,
    Map.of("x-message-ttl", 60_000,
           "x-dead-letter-exchange", "orders.dlx")));

// Important: queue-level TTL checks messages at the QUEUE HEAD
// Messages buried mid-queue are NOT expired until they reach the head
// (Per-message TTL can expire messages anywhere in the queue — checked on delivery)
4

Queue TTL & Auto-Expiry

x-expires deletes an unused queue after a given idle period; useful for temporary reply queues and ephemeral work queues that should not accumulate indefinitely.

  • x-message-ttl sets per-queue default TTL for messages in milliseconds.
  • Individual messages can override TTL via expiration property (string of ms).
  • When both queue TTL and per-message expiration are set, the lower value wins.
  • x-expires deletes the entire queue after an idle period — great for temporary reply queues.
  • Expired messages are dead-lettered to the x-dead-letter-exchange if configured, otherwise dropped.
  • TTL + DLX enables delay/retry patterns without requiring the delayed-message plugin.
Java — per-message TTL with DLX
// Queue-level TTL — all messages expire after 60 seconds
@Bean
public Queue orderQueue() {
    return QueueBuilder.durable("orders.processing")
        .withArgument("x-message-ttl", 60_000)     // 60 000 ms = 60 s
        .withArgument("x-dead-letter-exchange", "orders.dlx")   // where expired messages go
        .withArgument("x-dead-letter-routing-key", "orders.expired")
        .build();
}

// Per-message TTL — override on individual messages
rabbitTemplate.convertAndSend("orders", "order.placed", event, msg -> {
    msg.getMessageProperties().setExpiration("30000");  // 30 s (string, not int)
    return msg;
});

// Note: when BOTH queue TTL and per-message expiration are set,
// the LOWER of the two values wins (whichever expires first)
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/rabbitmq