RabbitMQ queue delay, delay push message


Scenarios

Current common application software has delayed pushed message shadow, it has found many applications, such as:

  • Taobao seven days automatically confirm receipt. After our receipt of goods, logistics system will delay seven days after sending a message to the payment system, payment system will notify shall call businesses, this process lasted seven days is to use the delay function push messaging middleware.
  • 12306 ticket payment confirmation page. We selected tickets click OK to jump page often there will be a countdown, representing the order is not confirmed within 30 minutes, then will automatically cancel the order. In fact, the moment of ordering ticketing service system will send a message to delay ordering system, delay 30 minutes to tell the order system order backlog, if we finish the order within 30 minutes, it can be judged by the logic code ignore the message out received.

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

  • Use redis set the expiration time to order, and finally by determining whether there redis order to decide whether the order has been completed. This solution compared to the low latency performance push message, because we know redis are stored in memory, we encountered a malicious orders or scalping will put huge pressure on memory.
  • Using traditional database polling to determine the status of an order in a database table, it will increase the number of IO, very low performance.
  • Use DelayQueue jvm native, it is also a memory-intensive, and there is no persistence strategy, or reboot the system immediately order information will be lost.

Message delay realization push

Before RabbitMQ 3.6.x we generally use the dead-letter queue + TTL expiration time to realize delay queue, we do not do too much introduction here, before you can refer to the article to understand: TTL, dead letter queue

The beginning of the RabbitMQ 3.6.x, RabbitMQ official queue delay plug-ins, you can download the plugins to be placed in the root directory of RabbitMQ. Delay plug-in download queue

First we create switches and message queues, application.properties configured the same as 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;

@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<String, Object> 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);
    }
}

We in the statement of Exchange can be set exchange.setDelayed(true)to open delay queue, you can also set the method of incoming switch statement is the following, since the underlying first way is the way to achieve this.

        //Map<String, Object> pros = new HashMap<>();
        //设置交换机支持延迟消息推送
        //pros.put("x-delayed-message", "topic");
        TopicExchange exchange = new TopicExchange(LAZY_EXCHANGE, true, false, pros);

When sending a message we need to specify a time delay pushed, here in the process of sending the incoming message parameters new MessagePostProcessor()in order to obtain Messagethe object, as required by Messagethe object api delay time is set.

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;

    //confirmCallback returnCallback 代码省略,请参照上一篇
  
    public void sendLazy(Object message){
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
        //id + 时间戳 全局唯一
        CorrelationData correlationData = new CorrelationData("12345678909"+new Date());

        //发送消息时指定 header 延迟时间
        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().setHeader("x-delay", "6000");
                message.getMessageProperties().setDelay(6000);
                return message;
            }
        }, correlationData);
    }
}

We can observe setDelay(Integer i)the underlying code, is disposed in the header of s-delay. We equivalent to manually set the header

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) {
        this.headers.remove(X_DELAY);
    }
    else {
        this.headers.put(X_DELAY, delay);
    }
}

End consumer for consumption

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

@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()));

    }

Test Results

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;

@SpringBootTest
@RunWith(SpringRunner.class)
public class MQSenderTest {

    @Autowired
    private MQSender mqSender;

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

        mqSender.sendLazy(msg + ":");
    }
}

Sure enough, 6 seconds after receipt of the message lazy receive hello spring boot:




Please indicate the source, thank you. https://www.cnblogs.com/haixiang/p/10966985.html

Guess you like

Origin www.cnblogs.com/haixiang/p/10966985.html