Backend Development 19 min read

Integrating RabbitMQ for Delayed Order Cancellation in a Spring Boot Mall Application

This tutorial walks through installing RabbitMQ, configuring it in a Spring Boot project, defining message models and enums, implementing delayed messaging to automatically cancel unpaid orders, and provides complete Java code, configuration files, testing steps, and useful resources for building a robust backend order system.

macrozheng
macrozheng
macrozheng
Integrating RabbitMQ for Delayed Order Cancellation in a Spring Boot Mall Application

Project Framework Introduction

RabbitMQ

RabbitMQ is a widely used open‑source message queue. It is lightweight, easy to deploy, supports multiple messaging protocols, and can be deployed in distributed and federated configurations to meet high‑scale, high‑availability requirements.

RabbitMQ Installation and Usage

1. Install Erlang. Download from http://erlang.org/download/otp (e.g., win64‑21.3.exe).

Erlang installation screenshot
Erlang installation screenshot

2. Install RabbitMQ. Download from https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe .

RabbitMQ installation screenshot
RabbitMQ installation screenshot

3. After installation, go to the

sbin

directory under the RabbitMQ installation folder.

RabbitMQ sbin directory
RabbitMQ sbin directory

4. Open a command line in the folder and enable the management plugin:

<code>rabbitmq-plugins enable rabbitmq_management</code>
Enable management plugin
Enable management plugin

5. Verify installation by visiting http://localhost:15672/ . Log in with username

guest

and password

guest

.

RabbitMQ management login
RabbitMQ management login

6. Create a user

mall

with password

mall

and assign the administrator role.

Create mall user
Create mall user

7. Create a new virtual host

/mall

.

Create virtual host
Create virtual host

8. Click the

mall

user to open the user configuration page and set permissions for the

/mall

virtual host.

Set permissions
Set permissions

9. At this point RabbitMQ installation and configuration are complete.

RabbitMQ Message Model

Message model diagram
Message model diagram

Producer (P) : Sends messages to an exchange.

Consumer (C) : Receives messages from a queue.

Exchange (X) : Receives messages from producers and routes them to queues based on routing keys.

Queue (Q) : Stores messages delivered by the exchange.

type : The exchange type, e.g.,

direct

routes messages directly according to the routing key (e.g.,

orange/black

).

Lombok

Lombok adds useful annotations to Java classes so you can avoid writing boilerplate getters, setters, etc. Install the Lombok plugin in IntelliJ IDEA and add the Lombok dependency in the project’s pom.xml .
Lombok usage
Lombok usage

Business Scenario Description

Cancel an order when the user’s order times out.

User places an order (locks inventory, applies coupons, points, etc.).

Generate the order and obtain the order ID.

Get the order timeout setting (e.g., 60 minutes).

Send a delayed message to RabbitMQ that will trigger order cancellation after the timeout.

If the user does not pay, cancel the order and release locked inventory, return coupons and points.

Integrating RabbitMQ for Delayed Messages

1. Add Dependencies in pom.xml

<code>&lt;!-- Message queue dependency --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-amqp&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;!-- Lombok dependency --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
    &lt;artifactId&gt;lombok&lt;/artifactId&gt;
    &lt;optional&gt;true&lt;/optional&gt;
&lt;/dependency&gt;</code>

2. Modify application.yml to Add RabbitMQ Settings

<code>rabbitmq:
  host: localhost # RabbitMQ host
  port: 5672        # RabbitMQ port
  virtual-host: /mall # Virtual host
  username: mall   # Username
  password: mall   # Password
  publisher-confirms: true # Enable publisher confirms for async messages</code>

3. Define Message Queue Enum QueueEnum

<code>package com.macro.mall.tiny.dto;

import lombok.Getter;

/**
 * Message queue enum configuration
 */
@Getter
public enum QueueEnum {
    /** Message notification queue */
    QUEUE_ORDER_CANCEL("mall.order.direct", "mall.order.cancel", "mall.order.cancel"),
    /** Message notification TTL (delay) queue */
    QUEUE_TTL_ORDER_CANCEL("mall.order.direct.ttl", "mall.order.cancel.ttl", "mall.order.cancel.ttl");

    private String exchange;
    private String name;
    private String routeKey;

    QueueEnum(String exchange, String name, String routeKey) {
        this.exchange = exchange;
        this.name = name;
        this.routeKey = routeKey;
    }
}</code>

4. RabbitMQ Configuration RabbitMqConfig

<code>package com.macro.mall.tiny.config;

import com.macro.mall.tiny.dto.QueueEnum;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Message queue configuration
 */
@Configuration
public class RabbitMqConfig {
    /** Order message actual consumption queue’s exchange */
    @Bean
    public DirectExchange orderDirect() {
        return ExchangeBuilder.directExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange())
                .durable(true)
                .build();
    }

    /** Order delay (TTL) queue’s exchange */
    @Bean
    public DirectExchange orderTtlDirect() {
        return ExchangeBuilder.directExchange(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange())
                .durable(true)
                .build();
    }

    /** Order consumption queue */
    @Bean
    public Queue orderQueue() {
        return new Queue(QueueEnum.QUEUE_ORDER_CANCEL.getName());
    }

    /** Order delay (TTL) queue – dead‑letter queue */
    @Bean
    public Queue orderTtlQueue() {
        return QueueBuilder.durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getName())
                .withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_ORDER_CANCEL.getExchange())
                .withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())
                .build();
    }

    /** Bind order queue to its exchange */
    @Bean
    public Binding orderBinding(DirectExchange orderDirect, Queue orderQueue) {
        return BindingBuilder.bind(orderQueue)
                .to(orderDirect)
                .with(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey());
    }

    /** Bind order delay queue to its exchange */
    @Bean
    public Binding orderTtlBinding(DirectExchange orderTtlDirect, Queue orderTtlQueue) {
        return BindingBuilder.bind(orderTtlQueue)
                .to(orderTtlDirect)
                .with(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey());
    }
}
</code>

5. Delayed Message Sender – CancelOrderSender

<code>package com.macro.mall.tiny.component;

import com.macro.mall.tiny.dto.QueueEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Sender for cancel‑order 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) {
        // Send delayed message to the TTL queue
        amqpTemplate.convertAndSend(
                QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(),
                QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(),
                orderId,
                new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        // Set expiration (delay) in milliseconds
                        message.getMessageProperties().setExpiration(String.valueOf(delayTimes));
                        return message;
                    }
                });
        LOGGER.info("send delay message orderId:{}", orderId);
    }
}
</code>

6. Cancel Order Receiver – CancelOrderReceiver

<code>package com.macro.mall.tiny.component;

import com.macro.mall.tiny.service.OmsPortalOrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Receiver for cancel‑order messages
 */
@Component
@RabbitListener(queues = "mall.order.cancel")
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>

7. Order Service Interface – OmsPortalOrderService

<code>package com.macro.mall.tiny.service;

import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.dto.OrderParam;
import org.springframework.transaction.annotation.Transactional;

/**
 * Front‑end order management service
 */
public interface OmsPortalOrderService {
    /**
     * Generate an order based on submitted information
     */
    @Transactional
    CommonResult generateOrder(OrderParam orderParam);

    /**
     * Cancel a single timed‑out order
     */
    @Transactional
    void cancelOrder(Long orderId);
}
</code>

8. Order Service Implementation – OmsPortalOrderServiceImpl

<code>package com.macro.mall.tiny.service.impl;

import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.component.CancelOrderSender;
import com.macro.mall.tiny.dto.OrderParam;
import com.macro.mall.tiny.service.OmsPortalOrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@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) {
        // TODO: implement order creation logic (refer to the original mall project)
        LOGGER.info("process generateOrder");
        // After order creation, send a delayed message to cancel the order if unpaid
        sendDelayMessageCancelOrder(11L); // example orderId
        return CommonResult.success(null, "Order placed successfully");
    }

    @Override
    public void cancelOrder(Long orderId) {
        // TODO: implement order cancellation logic (refer to the original mall project)
        LOGGER.info("process cancelOrder orderId:{}", orderId);
    }

    /**
     * Send a delayed message to cancel the order after the timeout period.
     */
    private void sendDelayMessageCancelOrder(Long orderId) {
        // Assume order timeout is 30 seconds for demonstration
        long delayTimes = 30 * 1000L;
        cancelOrderSender.sendMessage(orderId, delayTimes);
    }
}
</code>

9. Order Controller – OmsPortalOrderController

<code>package com.macro.mall.tiny.controller;

import com.macro.mall.tiny.dto.OrderParam;
import com.macro.mall.tiny.service.OmsPortalOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * Order management controller
 */
@Controller
@Api(tags = "OmsPortalOrderController", description = "Order Management")
@RequestMapping("/order")
public class OmsPortalOrderController {
    @Autowired
    private OmsPortalOrderService portalOrderService;

    @ApiOperation("Generate order based on shopping‑cart information")
    @RequestMapping(value = "/generateOrder", method = RequestMethod.POST)
    @ResponseBody
    public Object generateOrder(@RequestBody OrderParam orderParam) {
        return portalOrderService.generateOrder(orderParam);
    }
}
</code>

Interface Testing

Call the Order Generation Interface

Note: The delayed message time is set to 30 seconds.

Postman request
Postman request
Successful response
Successful response
RabbitMQ management view after message
RabbitMQ management view after message

Project Source Code

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

Recommended Reading

mall Architecture Overview

Learning Resources for mall

Integrating SpringBoot + MyBatis

Integrating Swagger‑UI for API Docs

Integrating Redis for Caching

SpringSecurity & JWT Authentication (Part 1)

SpringSecurity & JWT Authentication (Part 2)

SpringTask for Scheduled Jobs

Elasticsearch for Product Search

MongoDB for Document Operations

QR code
QR code

Follow us and give a like!

JavaSpring BootRabbitMQDelayed MessagingOrder Cancellation
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.