RabbitMQ: RabbitMQ delay queue, message delay push

Application scenario

At present, the common application software has the shadow of message delay push, which is also widely used, for example:

  • Taobao automatically confirms the receipt within seven days. After we sign in the goods, the logistics system will delay sending a message to the payment system seven days later to inform the payment system to send the payment to the merchant. This process lasts for seven days, which is to use the delayed push function of the message middleware.
  • 12306 ticket purchase payment confirmation page. We often have a countdown in the page of selecting tickets and clicking OK to jump, which means that if the order is not confirmed within 30 minutes, it will automatically cancel the order. In fact, at the moment when the order is placed, the ticketing business system will send a delay message to the order system, delaying 30 minutes to tell the order system that the order has not been completed. If we complete the order within 30 minutes, we can ignore the received consumption information through the logic code judgment.

In the above two scenarios, if we use the following two traditional solutions, we will undoubtedly greatly reduce the overall performance and throughput of the system:

  • Use redis to set the expiration time for the order. Finally, determine whether the order has been completed by judging whether there is still the order in redis. Compared with the delay push performance of messages, this solution has a lower performance, because we know that redis is stored in memory. When we encounter malicious orders or orders, it will bring huge pressure to memory.
  • The traditional database polling is used to judge the status of orders in the database table, which undoubtedly increases the number of IO, and the performance is extremely low.
  • Using the jvm's native DelayQueue also consumes a lot of memory, and there is no persistence policy, so the system will lose the order information when it goes down or restarts.

Implementation of message delay push

Before RabbitMQ 3.6.x, we usually used dead letter queue + TTL expiration time to implement delay queue. We will not introduce it here too much. Please refer to the previous articles to understand: TTL, dead letter queue

Starting from RabbitMQ 3.6.x, RabbitMQ officially provides a plug-in for delay queue, which can be downloaded (. ez) and placed in plugins under the root directory of RabbitMQ. Delay queue plug-in download

Enable plug-ins

# Open plug-in
rabbitmq-plugins enable rabbitmq_delayed_message_exchange

# Restart rabbitmq
/sbin/service rabbitmq-server restart

First we create switches and message queues, application.properties The configuration in is the same as in the previous article.

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;

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.#";

    public TopicExchange lazyExchange(){
        //Map<String, Object> pros = new HashMap<>();
        //Set switch to support delay message push
        //pros.put("x-delayed-message", "topic");
        TopicExchange exchange = new TopicExchange(LAZY_EXCHANGE, true, false, null);
        return exchange;

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

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

We can set the exchange.setDelayed(true) to open the delay queue, it can also be set to the following methods passed into the switch declaration, because the bottom layer of the first method is implemented in this way.

        //Map<String, Object> pros = new HashMap<>();
        //Set switch to support delay message push
        //pros.put("x-delayed-message", "topic");
        TopicExchange exchange = new TopicExchange(LAZY_EXCHANGE, true, false, pros);

When sending a Message, we need to specify the delay push time. Here, we pass in the parameter new MessagePostProcessor() in the method of sending a Message to get the Message object, because we need to use the api of the Message object to set the delay time.

Copyimport 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;

public class MQSender {

    private RabbitTemplate rabbitTemplate;

    //Confirm callback returncallback code is omitted, please refer to the previous article
    public void sendLazy(Object message){
        //id + timestamp globally unique
        CorrelationData correlationData = new CorrelationData("12345678909"+new Date());

        //Specify the header delay time when sending messages
        rabbitTemplate.convertAndSend(MQConfig.LAZY_EXCHANGE, "lazy.boot", message,
                new MessagePostProcessor() {
            public Message postProcessMessage(Message message) throws AmqpException {
                //Set message persistence
                //message.getMessageProperties().setHeader("x-delay", "6000");
                return message;
        }, correlationData);

We can observe the underlying code of setDelay(Integer i), and also set x-delay in the header. It's the same as setting the header manually

message.getMessageProperties().setHeader("x-delay", "6000");
 * Set the x-delay header.
 * @param delay the delay.
 * @since 1.6
public void setDelay(Integer delay) {
	if (delay == null || delay < 0) {
	else {
		this.headers.put(X_DELAY, delay);

Consumer consumption

Copyimport 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;
import java.util.Map;

public class MQReceiver {

    @RabbitListener(queues = "MQ.LazyQueue")
    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()));


test result

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

public class MQSenderTest {

    private MQSender mqSender;

    public void sendLazy() throws  Exception {
        String msg = "hello spring boot";

        mqSender.sendLazy(msg + ":");

As expected, after 6 seconds, I received the message lazy receive hello spring boot:

Tags: RabbitMQ Java Redis Database

Posted on Tue, 23 Jun 2020 06:10:01 -0400 by anish