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