RabbitMQ5/5--RabbitMQ delay queue optimization (plug-in installation)

Posted by harty83 on Sat, 15 Jan 2022 04:38:34 +0100

Normal delay queue

concept

The delay queue is orderly inside. The most important feature is reflected in its delay attribute. The elements in the delay queue are hope
After or before the specified time, the delay queue is simply used to store the data that needs to be processed at the specified time
Queue of elements.

Usage scenario

  • If the order is not paid within ten minutes, it will be automatically cancelled
  • If the newly created store has not uploaded products within ten days, it will automatically send a message reminder.
  • After successful registration, if the user does not log in within three days, a short message reminder will be sent.
  • The user initiates a refund, and if it is not handled within three days, notify the relevant operation personnel.
  • After the scheduled meeting, all participants shall be notified to attend the meeting ten minutes before the scheduled time point

Engineering Architecture

Code demonstration

config configuration class

package com.yhd.springbootrabbitmqttl.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Yuan Haodong
 * @description Demonstrates the queue configuration class
 * @date 2021/7/23 10:25
 */
@Configuration
public class TtlQueueConfig {
    public static final String X_EXCHANGE = "X";
    public static final String QUEUE_A = "QA";
    public static final String QUEUE_B = "QB";
    public static final String Y_DEAD_LETTER_EXCHANGE = "Y";
    public static final String DEAD_LETTER_QUEUE = "QD";
    public static final String QUEUE_C = "QC";

    // Declare xExchange
    @Bean("xExchange")
    public DirectExchange xExchange() {
        return new DirectExchange(X_EXCHANGE);
    }

    // Declare xExchange
    @Bean("yExchange")
    public DirectExchange yExchange() {
        return new DirectExchange(Y_DEAD_LETTER_EXCHANGE);
    }

    //Declare that queue A ttl is 10s and bind it to the corresponding dead letter switch
    @Bean("queueA")
    public Queue queueA() {
        Map<String, Object> args = new HashMap<>(3);
        //Declare the dead letter switch bound by the current queue
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        //Declare the dead letter routing key of the current queue
        args.put("x-dead-letter-routing-key", "YD");
        //Declare the TTL of the queue
        args.put("x-message-ttl", 10000);
        return QueueBuilder.durable(QUEUE_A).withArguments(args).build();
    }

    // Declare queue A bound X switch
    @Bean
    public Binding queueaBindingX(@Qualifier("queueA") Queue queueA,
                                  @Qualifier("xExchange") DirectExchange xExchange) {
        return BindingBuilder.bind(queueA).to(xExchange).with("XA");
    }

    //Declare queue B ttl as 40s and bind it to the corresponding dead letter switch
    @Bean("queueB")
    public Queue queueB() {
        Map<String, Object> args = new HashMap<>(3);
        //Declare the dead letter switch bound by the current queue
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        //Declare the dead letter routing key of the current queue
        args.put("x-dead-letter-routing-key", "YD");
        //Declare the TTL of the queue
        args.put("x-message-ttl", 40000);
        return QueueBuilder.durable(QUEUE_B).withArguments(args).build();
    }

    //Declare queue B binding X switch
    @Bean
    public Binding queuebBindingX(@Qualifier("queueB") Queue queue1B,
                                  @Qualifier("xExchange") DirectExchange xExchange) {
        return BindingBuilder.bind(queue1B).to(xExchange).with("XB");
    }

    //Declare dead letter queue QD
    @Bean("queueD")
    public Queue queueD() {
        return new Queue(DEAD_LETTER_QUEUE);
    }

    //Declare dead letter queue QD binding relationship
    @Bean
    public Binding deadLetterBindingQAD(@Qualifier("queueD") Queue queueD,
                                        @Qualifier("yExchange") DirectExchange yExchange) {
        return BindingBuilder.bind(queueD).to(yExchange).with("YD");
    }


    //Declare queue C dead letter switch
    @Bean("queueC")
    public Queue queueC() {
        Map<String, Object> args = new HashMap<>(3);
        //Declare the dead letter switch bound by the current queue
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        //Declare the dead letter routing key of the current queue
        args.put("x-dead-letter-routing-key", "YD");
        //TTL attribute is not declared
        return QueueBuilder.durable(QUEUE_C).withArguments(args).build();
    }

    //Declare queue B binding X switch
    @Bean
    public Binding queuecBindingX(@Qualifier("queueC") Queue queueC,
                                  @Qualifier("xExchange") DirectExchange xExchange) {
        return BindingBuilder.bind(queueC).to(xExchange).with("XC");
    }
}

producer

package com.yhd.springbootrabbitmqttl.confroller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * @author Yuan Haodong
 * @description
 * @date 2021/7/23 11:00
 */
@Slf4j
@RequestMapping("ttl")
@RestController
public class SendMsgController {
    @Autowired
     RabbitTemplate rabbitTemplate;
        @GetMapping("sendMsg/{message}")
        public void sendMsg(@PathVariable String message){
        log.info("Current time:{},Send a message to two TTL queue:{}", new Date(), message);
        rabbitTemplate.convertAndSend("X", "XA", "Message from ttl For 10 S Queue of: "+message);
        rabbitTemplate.convertAndSend("X", "XB", "Message from ttl For 40 S Queue of: "+message);
    }

    @GetMapping("sendExpirationMsg/{message}/{ttlTime}")
    public void sendMsg(@PathVariable String message,@PathVariable String ttlTime) {
        rabbitTemplate.convertAndSend("X", "XC", message, correlationData->{
            correlationData.getMessageProperties().setExpiration(ttlTime);
        return correlationData;
});
        log.info("Current time:{},Length of time to send a message{}millisecond TTL Message to queue C:{}", new Date(),ttlTime, message);
    }
}

consumer

package com.yhd.springbootrabbitmqttl.consumer;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Date;

/**
 * @author Yuan Haodong
 * @description
 * @date 2021/7/23 11:02
 */
@Slf4j
@Component
public class DeadLetterQueueConsumer {
    @RabbitListener(queues = "QD")
    public void receiveD(Message message, Channel channel) throws IOException
    {String msg = new String(message.getBody());
        log.info("Current time:{},Received dead letter queue information{}", new Date().toString(), msg);
    }
}

Browser input address test

http://localhost:8088/ttl/sendExpirationMsg / Hello 1 / 20000
http://localhost:8088/ttl/sendExpirationMsg / Hello 2 / 2000

Existing problems

If the message expiration time TTL is declared, access in sequence (sequential access). If the delay time of the first access is longer than that of the subsequent queue, the subsequent queue will be blocked. Even if the delay time of the subsequent queue is short, it will not be executed until the first execution is completed

Install plug-in rabbitmq_delayed_message_exchange

1. Download

Official website download link https://www.rabbitmq.com/community-plugins.html , Download
rabbitmq_delayed_message_exchange plug-in, and then unzip and place it in the plug-in directory of RabbitMQ.

RabbitMQ default installation directory location

usr/lib/rabbitmq/lib/rabbitmq_server-3.7.18

2. Enter the plugins directory

cd /usr/lib/rabbitmq/lib/rabbitmq_server-3.7.18/plugins

3. Enter instructions to make the plug-in effective

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

4. Restart the RabbitMQ service

systemctl restart rabbitmq-server

5. The browser accesses the RabbitMQ client

6. Check whether the plug-in has been successfully installed

Before installation

After installation

Delay queue code after plug-in installation

config configuration class

package com.yhd.springbootrabbitmqttl.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Yuan Haodong
 * @description
 * @date 2021/7/23 15:58
 */
@Configuration
public class DelayedQueueConfig {
    public static final String DELAYED_QUEUE_NAME = "delayed.queue";
    public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
    public static final String DELAYED_ROUTING_KEY = "delayed.routingkey";

    //Declaration queue
    @Bean
    public Queue delayedQueue() {
        return new Queue(DELAYED_QUEUE_NAME);
    }
    //Custom switch what we define here is a delay switch
    @Bean
    public CustomExchange delayedExchange()
    { Map<String, Object> args = new HashMap<>();
        //Custom switch type
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false,
                args);
    }

    //Relational binding
    @Bean
    public Binding bindingDelayedQueue(@Qualifier("delayedQueue") Queue queue,
                                       @Qualifier("delayedExchange") CustomExchange
                                               delayedExchange) {
        return BindingBuilder.bind(queue).to(delayedExchange).with(DELAYED_ROUTING_KEY).noargs();
    }
}

Producer (SendMsgController append code)

public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
public static final String DELAYED_ROUTING_KEY = "delayed.routingkey";
@GetMapping("sendDelayMsg/{message}/{delayTime}")
public void sendMsg(@PathVariable String message,@PathVariable Integer delayTime) {
    rabbitTemplate.convertAndSend(DELAYED_EXCHANGE_NAME, DELAYED_ROUTING_KEY, message,
            correlationData ->{
                correlationData.getMessageProperties().setDelay(delayTime);
                return correlationData;
            });
    log.info(" Current time: {}, Send a delay message {} Milliseconds of information to the queue delayed.queue:{}", new
            Date(),delayTime, message);
}

Consumer (add code to DeadLetterQueueConsumer)

public static final String DELAYED_QUEUE_NAME = "delayed.queue";
@RabbitListener(queues = DELAYED_QUEUE_NAME)
public void receiveDelayedQueue(Message
                                        message) {
    String msg = new
            String(message.getBody());
    log.info("Current time:{},Message received from delay queue:{}", new Date().toString(), msg);
}

Browser access

http://localhost:8088/ttl/sendDelayMsg/come on baby1/20000

http://localhost:8088/ttl/sendDelayMsg/come on baby2/2000

Operation results

summary

  • After using the delay queue plug-in, the blocking problem of sending messages in sequence based on dead letter queue is solved
  • Messages are sent according to the delay time. Messages with a long delay time are sent first and will not block messages with a short delay time later

Topics: RabbitMQ