Backend Development 8 min read

Implementing Delayed Message Delivery with RabbitMQ and Spring Boot

This article explains how to implement delayed message delivery in RabbitMQ using the official delayed‑queue plugin and Spring Boot, covering configuration of exchanges, queues, bindings, message publishing with delay headers, and consumer handling, along with code examples and test results.

Architect
Architect
Architect
Implementing Delayed Message Delivery with RabbitMQ and Spring Boot

Delayed message delivery is widely used in e‑commerce scenarios such as automatic order confirmation after a fixed period, for example Taobao's seven‑day auto‑confirm and 12306's 30‑minute order timeout.

Traditional solutions like using Redis expiration, database polling, or JVM DelayQueue suffer from performance bottlenecks, memory pressure, or lack of persistence.

RabbitMQ provides a delayed‑queue plugin (available from version 3.6.x) that enables true delayed messaging without those drawbacks.

Configuration steps include defining a topic exchange that supports delayed messages, a durable queue, and a binding with a routing key.

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);
    }
}

When sending a message, the delay is specified either by calling Message.setDelay(Integer) or by adding the x-delay header via a MessagePostProcessor .

import com.anqi.mq.config.MQConfig;
import org.springframework.amqp.AmqpException;
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 listens on the lazy queue, acknowledges the message, and processes the payload after the configured delay.

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
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 simple string with a 6000 ms delay; the console shows the message being received after roughly six seconds, confirming the delayed‑queue functionality.

JavaSpring Bootmessage queueRabbitMQDelayed Messaging
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

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.