Backend Development 8 min read

Implementing Delayed Message Queues with RabbitMQ and Spring Boot

This article explains how to implement delayed message delivery in RabbitMQ using the delayed‑message‑exchange plugin and Spring Boot, covering the shortcomings of traditional approaches, configuration of exchanges and queues, sending messages with delay headers, and a test demonstrating a 6‑second delayed receipt.

Top Architect
Top Architect
Top Architect
Implementing Delayed Message Queues with RabbitMQ and Spring Boot

Many real‑world applications need delayed message delivery, such as Taobao's automatic receipt confirmation after seven days and 12306's order cancellation after a 30‑minute countdown. Traditional solutions like Redis expiration, database polling, or JVM DelayQueue suffer from low performance, high memory pressure, or lack of persistence.

Since RabbitMQ 3.6.x, the official delayed‑message‑exchange plugin provides a native way to implement delayed queues. The plugin can be downloaded from GitHub and placed in the plugins directory.

Configuration is done in a Spring Boot MQConfig class, where a topic exchange with exchange.setDelayed(true) is declared, along with a durable queue and binding:

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MQConfig {
    public static final String LAZY_EXCHANGE = "Ex.LazyExchange";
    public static final String LAZY_QUEUE = "MQ.LazyQueue";
    public static final String LAZY_KEY = "lazy.#";

    @Bean
    public TopicExchange lazyExchange() {
        //Map
pros = new HashMap<>();
        //pros.put("x-delayed-message", "topic");
        TopicExchange exchange = new TopicExchange(LAZY_EXCHANGE, true, false, pros);
        exchange.setDelayed(true);
        return exchange;
    }

    @Bean
    public Queue lazyQueue() {
        return new Queue(LAZY_QUEUE, true);
    }

    @Bean
    public Binding lazyBinding() {
        return BindingBuilder.bind(lazyQueue()).to(lazyExchange()).with(LAZY_KEY);
    }
}

To send a delayed message, the MQSender component uses MessagePostProcessor to set the Message properties, including persistence and the delay (e.g., 6000 ms):

import com.anqi.mq.config.MQConfig;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component
public class MQSender {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendLazy(Object message) {
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
        CorrelationData correlationData = new CorrelationData("12345678909" + new Date());
        rabbitTemplate.convertAndSend(MQConfig.LAZY_EXCHANGE, "lazy.boot", message,
            new MessagePostProcessor() {
                @Override
                public Message postProcessMessage(Message message) throws AmqpException {
                    message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                    message.getMessageProperties().setDelay(6000);
                    return message;
                }
            }, correlationData);
    }
}

The consumer side is a simple MQReceiver annotated with @RabbitListener that acknowledges the message and prints its content:

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.core.Message;
import org.springframework.stereotype.Component;
import java.io.IOException;

@Component
public class MQReceiver {
    @RabbitListener(queues = "MQ.LazyQueue")
    @RabbitHandler
    public void onLazyMessage(Message msg, Channel channel) throws IOException {
        long deliveryTag = msg.getMessageProperties().getDeliveryTag();
        channel.basicAck(deliveryTag, true);
        System.out.println("lazy receive " + new String(msg.getBody()));
    }
}

A JUnit test sends a sample message and, after six seconds, the console outputs lazy receive hello spring boot: , confirming that the delayed delivery works as expected.

Internally, setting the delay ultimately adds the x-delay header to the message, which the RabbitMQ plugin interprets to postpone delivery.

backend-developmentSpring BootMessage QueueRabbitMQDelayed Queue
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.