← Back to list

Rabbit MQ Dead LetterQueue(DLQ) and Retry Logic with SpringBoot Application Part 4

In this tutorial i will walk through on Dead Letter Queue Implementation in Spring Boot application

Suhas Prabhu · 2026-04-29 10:05 · 0 claps · 1.8 min read
#rabbitmq #java #dead-letter-queue #retry
Open on Medium ↗

Rabbit MQ Dead LetterQueue(DLQ) and Retry Logic with SpringBoot Application Part 4

In this tutorial i will walk through on Dead Letter Queue Implementation in Spring Boot application

Topics Covered

  1. Dead Letter Queue(DLQ)
  2. Retry Logic

Dead Letter Queue(DLQ)

illustration of Simple DLQ

illustration of Simple DLQ

Why we need DLQ ? in previous tutorial we observed thatwhen a message is poisoned or corrupt or server down or network issue or any other reason we acknowledgement protocol , by using the same protocol we can create DLQ to store all the dead messages or Rejected Messages which can later sent to a Logger.

Producer Config

    //main queue connected to dlq and exchange with durable 
    @Bean
    public Queue mainQueue(){
       return QueueBuilder.durable("mainQueue").withArgument("x-dead-letter-exchange", "dlqExchange")
                .withArgument("x-dead-letter-routing-key", "deadLetterQueueRoutingKey").build();
    }

    @Bean
    public DirectExchange mainQueueExchange(){
        return new DirectExchange("mainQueueExchange");
    }

    @Bean
    public Binding mainQueueExchangeBinding(){
        return BindingBuilder.bind(mainQueue()).to(mainQueueExchange()).with("mainQueueExchangeRoutingKey");
    }

    @Bean
    public Queue DLQ(){
        return QueueBuilder.durable("deadLetterQueueForMainQueue").build();
    }

    @Bean
    public DirectExchange DLQExchange(){
        return new DirectExchange("dlqExchange");
    }

    @Bean
    public Binding DLQBinding(){
        return BindingBuilder.bind(DLQ()).to(DLQExchange()).with("deadLetterQueueRoutingKey");
    }

Consumer Config (RETRY LOGIC)

when this retry logic comes into play when there is exception thrown from customer without first acknowledging (NACK and requeue =true)the message the broker will keep trying to send messages after maximum attempts it will send all failed messages to DLQ(NACK and requeue =false).

we have two types in retry Stateless and Stateful

Stateless->retry happens in memory of consumer and NACKs happen at the end. Stateful->retry happens at broker level and NACKs happen at each interval.

STATELESS Config

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactoryWithRetry(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setAdviceChain(RetryInterceptorBuilder.stateless()
                .maxRetries(5)
                .backOffOptions(1000, 2.0, 10000) // initialInterval, multiplier, maxInterval
                .recoverer(new RejectAndDontRequeueRecoverer())
                .build());
        return factory;
    }

initialInterval is the seconds after initial failure to deliver message to consumer multiplier multiplies the initial interval time after 1st failure for 2nd failure interval time ex-> 1st fails 1sec interval , 2nd fail 1x2 = 2secs interval then 2x2 = 4 sec then 4 x 2 = 8secs then 8x 2 =16 but capped at 10secs (maxInterval)

After max retries the messages will be sent DLQ.

STATEFUL Config

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactoryWithStateFulRetry(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setErrorHandler(throwable ->
                LOGGER.error("Consumer error: {}", throwable.getMessage()));
        factory.setDefaultRequeueRejected(false);// safety net if there is no retry configured and need to requeue the message if exception occurs from consumer
        factory.setAdviceChain(RetryInterceptorBuilder.stateful()
                .maxRetries(5)
                .messageKeyGenerator(message -> message.getMessageProperties().getMessageId()) //using message id to track during stateful only works when the producer sends message id if not it breaks
                .backOffOptions(1000, 2.0, 10000) // initialInterval, multiplier, maxInterval
                .recoverer(new RepublishMessageRecoverer(
                        rabbitTemplate, "dlq_exchange", "routingkeyForDLQ")) // recover and redirect the message to DLQ
                .build());
        return factory;
    }

Git Repo https://github.com/SuhasPrabhu26VP/rabbitmq-simplequeue-springboot-example


메타데이터
post_id
fae57ac49d7e
slug
rabbit-mq-dead-letterqueue-dlq-and-retry-logic-with-springboot-application-part-4-fae57ac49d7e
url
https://medium.com/@suhasprabhu.vp/rabbit-mq-dead-letterqueue-dlq-and-retry-logic-with-springboot-application-part-4-fae57ac49d7e
canonical_url
https://medium.com/@suhasprabhu.vp/rabbit-mq-dead-letterqueue-dlq-and-retry-logic-with-springboot-application-part-4-fae57ac49d7e
author_url
https://medium.com/@suhasprabhu.vp
status
ok
fetched_at
2026-07-10 22:52:34