Backend Development 12 min read

How to Implement RabbitMQ Delayed Messages with the Delayed Plugin in Spring Boot

This guide walks through installing the RabbitMQ delayed‑message‑exchange plugin, configuring it in a Spring Boot application, and building Java components to send and receive delayed order‑cancellation messages, comparing the plugin approach with dead‑letter queues and providing complete code snippets and deployment steps.

macrozheng
macrozheng
macrozheng
How to Implement RabbitMQ Delayed Messages with the Delayed Plugin in Spring Boot
RabbitMQ implements delayed messages in two ways: using a dead‑letter queue or using the delayed‑message‑exchange plugin . The dead‑letter method has been covered elsewhere; this article focuses on the simpler plugin approach.

Preparation

To follow this tutorial you should already be familiar with RabbitMQ. If not, see the article "RabbitMQ Practical Tips".

Plugin Installation

First download and install the RabbitMQ delayed‑message‑exchange plugin.

Download the plugin from the official RabbitMQ site: https://www.rabbitmq.com/community-plugins.html

Or search for

rabbitmq_delayed_message_exchange

and download the version that matches your RabbitMQ installation.

Copy the plugin file into the

plugins

directory of your RabbitMQ installation.

In the

sbin

directory, enable the plugin with the command:

<code>rabbitmq-plugins enable rabbitmq_delayed_message_exchange</code>

After successful enablement, restart the RabbitMQ service.

Implement Delayed Messages

We will implement delayed messages in a Spring Boot project using an order‑cancellation scenario: if a user does not pay within 60 minutes, the order is automatically cancelled.

Add the AMQP starter dependency to

pom.xml

:

<code><!--消息队列相关依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</code>

Configure RabbitMQ in

application.yml

:

<code>spring:
  rabbitmq:
    host: localhost # rabbitmq host
    port: 5672       # rabbitmq port
    virtual-host: /mall # virtual host
    username: mall   # username
    password: mall   # password
    publisher-confirms: true # enable confirms for async messages
</code>

Create a Java configuration class to define the custom exchange, queue, and binding:

<code>/**
 * Message queue configuration
 */
@Configuration
public class RabbitMqConfig {
    /**
     * Custom exchange for delayed messages
     */
    @Bean
    CustomExchange orderPluginDirect() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getExchange(), "x-delayed-message", true, false, args);
    }

    /**
     * Queue for delayed order cancellation
     */
    @Bean
    public Queue orderPluginQueue() {
        return new Queue(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getName());
    }

    /**
     * Bind the queue to the custom exchange
     */
    @Bean
    public Binding orderPluginBinding(CustomExchange orderPluginDirect, Queue orderPluginQueue) {
        return BindingBuilder.bind(orderPluginQueue)
                             .to(orderPluginDirect)
                             .with(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getRouteKey())
                             .noargs();
    }
}
</code>

Sender component that publishes a message with the

x-delay

header:

<code>/**
 * Sender for order‑cancellation messages
 */
@Component
public class CancelOrderSender {
    private static final Logger LOGGER = LoggerFactory.getLogger(CancelOrderSender.class);
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendMessage(Long orderId, final long delayTimes) {
        amqpTemplate.convertAndSend(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getExchange(),
                                   QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getRouteKey(),
                                   orderId,
                                   message -> {
                                       message.getMessageProperties().setHeader("x-delay", delayTimes);
                                       return message;
                                   });
        LOGGER.info("send delay message orderId:{}", orderId);
    }
}
</code>

Receiver component that processes delayed messages:

<code>/**
 * Receiver for order‑cancellation messages
 */
@Component
@RabbitListener(queues = "mall.order.cancel.plugin")
public class CancelOrderReceiver {
    private static final Logger LOGGER = LoggerFactory.getLogger(CancelOrderReceiver.class);
    @Autowired
    private OmsPortalOrderService portalOrderService;

    @RabbitHandler
    public void handle(Long orderId) {
        LOGGER.info("receive delay message orderId:{}", orderId);
        portalOrderService.cancelOrder(orderId);
    }
}
</code>

Modify the order service to send a delayed cancellation message after an order is placed:

<code>/**
 * Front‑end order service implementation
 */
@Service
public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
    private static final Logger LOGGER = LoggerFactory.getLogger(OmsPortalOrderServiceImpl.class);
    @Autowired
    private CancelOrderSender cancelOrderSender;

    @Override
    public CommonResult generateOrder(OrderParam orderParam) {
        // ... order creation logic ...
        LOGGER.info("process generateOrder");
        // Send a 30‑second delayed cancellation message (for demo)
        sendDelayMessageCancelOrder(11L);
        return CommonResult.success(null, "Order placed successfully");
    }

    @Override
    public void cancelOrder(Long orderId) {
        // ... order cancellation logic ...
        LOGGER.info("process cancelOrder orderId:{}", orderId);
    }

    private void sendDelayMessageCancelOrder(Long orderId) {
        long delayTimes = 30 * 1000; // 30 seconds for testing
        cancelOrderSender.sendMessage(orderId, delayTimes);
    }
}
</code>

Start the application and invoke the order‑creation API via Swagger.

Check the console logs; the receive log appears after the configured 30‑second delay.

<code>2020-06-08 13:46:01.474  INFO 1644 --- [nio-8080-exec-1] c.m.m.t.s.i.OmsPortalOrderServiceImpl    : process generateOrder
2020-06-08 13:46:01.482  INFO 1644 --- [nio-8080-exec-1] c.m.m.tiny.component.CancelOrderSender   : send delay message orderId:11
2020-06-08 13:46:31.517  INFO 1644 --- [cTaskExecutor-4] c.m.m.t.component.CancelOrderReceiver    : receive delay message orderId:11
2020-06-08 13:46:31.520  INFO 1644 --- [cTaskExecutor-4] c.m.m.t.s.i.OmsPortalOrderServiceImpl    : process cancelOrder orderId:11
</code>

Comparison of Two Implementations

We previously used the dead‑letter‑queue method; here we compare both approaches.

Dead Letter Queue

A dead‑letter queue forwards messages that exceed a TTL to another queue, enabling delayed processing.

Delayed Plugin

Installing the plugin creates a custom exchange that can delay message delivery directly.

Conclusion

The dead‑letter method requires two exchanges and two queues, whereas the delayed‑plugin method needs only one exchange and one queue, making the plugin approach simpler to use.

Project Source Code

https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-delay

Recommended Reading

RabbitMQ Practical Tips

Why Program to Interfaces?

SQL Optimization Interview Tips

IDEA‑like Database Management Tool Review

15 Developer Tools Used by Alibaba Engineers

Open‑Source Git Service Project with 34K Stars

Tips for Optimizing a Slow PC After IDEA Update

Automation Deployment Tricks I Use Frequently

Spring Cloud Hands‑On Project Recommendation

My Open‑Source Projects: From 0 to 20K Stars

Backend DevelopmentpluginSpring Bootmessage queueRabbitMQDelayed Messages
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

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.