Backend Development 8 min read

Mastering RabbitMQ Delayed and Priority Queues with Spring Boot

This guide explains how to implement delayed and priority queues in RabbitMQ 3.8 using Spring Boot 2.6, covering dead‑letter exchange configuration, message expiration, priority settings, code examples for sending and consuming messages, and practical considerations such as priority limits and ordering behavior.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering RabbitMQ Delayed and Priority Queues with Spring Boot

Environment

Spring Boot 2.6.12 + RabbitMQ 3.8.12 + Erlang 23.2.5

1. Delayed Queue

A delayed queue means a message becomes visible to consumers only after a specified time.

Implementation uses a dead‑letter exchange (DLX):

Create a regular exchange and queue (e.g., simple.exchange , simple.queue ) and set the x-dead-letter-exchange argument to point to the DLX.

Create the dead‑letter exchange and queue (e.g., dlx.exchange , dlx.queue ).

When sending a message, set its TTL (expiration). Once the TTL expires, the broker forwards the message to the DLX.

Messages are dead‑lettered when:

The message is rejected with requeue=false .

The message expires (TTL exceeded).

The queue reaches its maximum length.

Code to send a delayed message:

<code>@Resource
private RabbitTemplate rabbitTemplate;

public Object testSender() {
    this.rabbitTemplate.convertAndSend(
        "simple.direct",
        "",
        "Delayed message - " + System.currentTimeMillis(),
        message -> {
            // set TTL to 10 seconds
            message.getMessageProperties().setExpiration("10000");
            return message;
        });
    logger.info(">>>>>>>>> Message sent successfully");
    return "success";
}
</code>

Listener for the dead‑letter queue:

<code>@RabbitListener(queues = {"dlx.queue"})
public void dlxMessage(String message, Message msg, Channel channel) throws IOException {
    try {
        System.out.println(new String(msg.getBody()));
        logger.info(">>>>>>>>> Received message - {}, messageId: {}", message, msg.getMessageProperties().getMessageId());
        channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
    } catch (Exception e) {
        logger.error("Message receive exception: {}", e.getMessage());
        channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, false);
    }
}
</code>

Key points for delayed queues:

Configure the queue with a dead‑letter exchange.

Set a TTL (expiration) on each message.

2. Priority Queue

Priority queues are supported from RabbitMQ 3.5.0 onward.

Set the queue’s maximum priority with the x-max-priority argument (value 1‑255, typically 1‑10 for efficiency).

Create a direct exchange (e.g., priority-exchange ) and bind a queue (e.g., priority-queue ) to it.

Set x-max-priority to 100 (maximum allowed is 255). Higher values increase Erlang process usage and may affect scheduling.

Sending messages with a specific priority:

<code>@GetMapping("/sendPriority")
public Object sendPriority(String msg, Integer priority) {
    ms.sendPriorityQueue(msg, priority);
    return "success";
}

public void sendPriorityQueue(String msg, Integer priority) {
    logger.info("Preparing to send message: {}", msg);
    Message message = MessageBuilder.withBody(msg.getBytes())
                                 .setPriority(priority)
                                 .build();
    rabbitTemplate.convertAndSend("priority-exchange", "pe.msg", message);
}
</code>

Example messages:

<code>// Message 1
msg=First message&priority=2
// Message 2
msg=Second message&priority=10
// Message 3
msg=Third message&priority=1
// Message 4
msg=Fourth message&priority=7
</code>

Listener for the priority queue:

<code>@RabbitListener(queues = {"priority-queue"})
@RabbitHandler
public void listenerPriority(Message message, Channel channel) {
    String content = new String(message.getBody(), Charset.forName("UTF-8"));
    MessageProperties props = message.getMessageProperties();
    try {
        System.out.println("Received from exchange: " + props.getReceivedExchange() + ", queue: " + props.getConsumerQueue() + ": " + content);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
    } catch (Exception e) {
        e.printStackTrace();
        try {
            channel.basicReject(props.getDeliveryTag(), false);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}
</code>

When the service runs, messages are consumed in the order of their priority (higher priority first). If a message’s priority exceeds the queue’s maximum, RabbitMQ treats it as having the maximum priority. Testing with priorities above 100 or 255 resulted in unordered delivery.

Messages without a priority property are treated as if their priority were 0. Messages with a priority higher than the queue's maximum are treated as if they were published with the maximum priority.

End of tutorial.

JavaSpring BootMessage QueueRabbitMQPriority QueueDelayed Queue
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

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.