Spring boot 2.3 integrates RabbitMQ to realize delayed consumption message

Posted by Sh0t on Tue, 26 May 2020 15:43:03 +0200

1. Source code access address

Source code address at the end of the article
https://www.sunnyblog.top/detail.html?id=1265257400324063232
This chapter mainly realizes the delayed consumption of messages. Before learning the delayed consumption, we must first understand the two basic concepts of RabbitMQ, TTL and dead letter Exchange of messages, and realize the delayed consumption of messages through the combination of the two concepts.
If you don't want to see the explanation of the principle, you can directly see the code implementation through title 6

2. TTL of message (time to live)

The TTL of a message is the lifetime of the message. RabbitMQ can set TTL for queues and messages respectively. Setting the queue is the retention time of the queue without the connection of consumers. You can also set each individual message separately. Beyond this time, we think the news is dead, which is called the dead letter.

3. Dead Letter Exchanges

  • A message will enter the dead letter route when the following conditions are met. Remember that this is a route rather than a queue. A route can correspond to many queues.
  • A message is rejected by the Consumer, and the request in the parameter of the reject method is false. That is to say, it will not be put in the queue again and used by other consumers.
  • The TTL of the above message has arrived, and the message has expired
  • The queue length limit is full. Messages in the front row will be discarded or thrown to the dead letter route. Dead Letter Exchange is actually a kind of ordinary exchange, just like creating other exchanges. Only when a message expires in a queue with Dead Letter Exchange set, the message forwarding will be automatically triggered and sent to Dead Letter Exchange.

4. Principle of delayed consumption

  • General principle: first, send the message to the dead letter queue. The dead letter queue sets the ttl expiration time. After expiration, the message will be automatically sent to the general queue to realize message consumption
  • The implementation steps are as follows
  • Create a dead letter exchanger
  • Create dead letter queue
  • Binding the dead letter queue to the dead letter switch cannot be arbitrary. Instead, the sender of a routingkey message must also specify the routingkey of the message when sending messages to exchange. Exchange no longer gives messages to each bound queue, but judges based on the routing key of the message. Only when the routing key of the queue is completely consistent with the routing key of the message, can the message be received
  • Create normal switch
  • Create normal queue
  • Bind a normal queue to a normal switch

5. Delay consumption of message based on case

Here we take the most familiar 12306 ticket purchase as an example to analyze the case scenario. The steps of 12306 ticket purchase are as follows:

  • First log in 12306 and place the order according to the date, starting station and other conditions
  • The order is not paid and the payment time is 30 minutes
  • Here you can use the delay queue to send the order number to the dead letter queue of MQ when the order is finished, and set the expiration time of 30 minutes. After 30 minutes, the data of the dead letter queue will be forwarded to the normal queue, and the order number of the order will be obtained from the normal queue. Then we can query the payment status of the order according to the order number. If we have paid, we will not do anything, If the order is cancelled without payment, the payment status will be closed and the ticket will be rolled back to the ticket pool for other users to purchase

6. Code implementation

  • Creating queues, switches, and binding relationships in RabbitMQConfig

      @Configuration
      public class RabbitMQConfig {
    
      		/**
      		 * Test sending message to MQ
      		 * @return
      		 */
      		@Bean
      		public Queue testHello() {
      				return new Queue(SysConstant.QUEUE_TEST_HELLO);
      		}
    
    
      		/**
      		 * Dead letter switch
      		 * @return
      		 */
      		@Bean
      		public DirectExchange sysOrderDelayExchange() {
      				return new DirectExchange(SysConstant.SYS_ORDER_DELAY_EXCHANGE);
      		}
    
      		/**
      		 * Dead letter queue
      		 * @return
      		 */
      		@Bean
      		public Queue sysOrderDelayQueue() {
      				Map<String, Object> map = new HashMap<String, Object>(16);
      				map.put("x-dead-letter-exchange",SysConstant.SYS_ORDER_RECEIVE_EXCHANGE); //Specify the switch sent by dead letter
      				map.put("x-dead-letter-routing-key", SysConstant.SYS_ORDER_RECEIVE_KEY); //Specify the routingkey of dead letter
      				return new Queue(SysConstant.SYS_ORDER_DELAY_QUEUE, true, false, false, map);
      		}
    
      		/**
      		 * Bind dead letter switch to dead letter queue
      		 * @return
      		 */
      		@Bean
      		public Binding sysOrderDelayBinding() {
      				return BindingBuilder.bind(sysOrderDelayQueue()).to(sysOrderDelayExchange()).with(SysConstant.SYS_ORDER_DELAY_KEY);
      		}
    
      		/**
      		 * Dead letter receiving switch for receiving messages of dead letter queue
      		 * @return
      		 */
      		@Bean
      		public DirectExchange sysOrderReceiveExchange() {
      				return new DirectExchange(SysConstant.SYS_ORDER_RECEIVE_EXCHANGE);
      		}
    
      		/**
      		 * Dead letter receiving queue
      		 * @return
      		 */
      		@Bean
      		public Queue sysOrderReceiveQueue() {
      				return new Queue(SysConstant.SYS_ORDER_RECEIVE_QUEUE);
      		}
    
      		/**
      		 * Dead letter receiving switch binding receiving dead letter queue consumption queue
      		 * @return
      		 */
      		@Bean
      		public Binding sysOrdeReceiveBinding() {
      				return BindingBuilder.bind(sysOrderReceiveQueue()).to(sysOrderReceiveExchange()).with(SysConstant.SYS_ORDER_RECEIVE_KEY);
      		}
      }
    
  •   Method of sending delay message to dead exchange
    
      		@Service
      		public class MsgService {
    
      				@Autowired
      				private RabbitTemplate rabbitTemplate;
      				/**
      				 * Send delay message to mq
      				 * @param exchange Dead letter switch
      				 * @param routeKey Routing key
      				 * @param data send data
      				 * @param delayTime Expiration time in milliseconds
      				 */
      				public void sendDelayMsgToMQ(String exchange, String routeKey, String data,int delayTime) {
      						rabbitTemplate.convertAndSend(exchange, routeKey, data, message -> {
      								message.getMessageProperties().setExpiration(delayTime + "");
      								return message;
      						});
      				}
      		}
    
  • Listening to queue messagesreceivemsglestener class

       /**
      		 * Acquired delay message
      		 * Here, a message is received for corresponding business processing (for example: cancel order, close payment, roll back inventory, etc.)
      		 * @param msg
      		 */
      		@RabbitListener(queues = SysConstant.SYS_ORDER_RECEIVE_QUEUE)
      		@RabbitHandler
      		public void getdelayMsg(String msg) {
      				log.info("MQ Received message time:{},Message content:{}", DateUtil.formatDateTime(DateUtil.date()),msg);
      				log.info("------->Here, the business logic of order closing, payment closing and inventory rollback is realized...");
      		}
    
  •   		establish Controller Send a message to the queue and set the expiration time of 10 seconds
    
      				@RestController
      				@RequestMapping("mq")
      				@Slf4j
      				public class MQController {
    
      						@Autowired
      						private MsgService msgService;
    
      						@GetMapping("sendMsg")
      						public String sendMsg() {
      								log.info("Send delay message time:" + DateUtil.formatDateTime(DateUtil.date()));
    
      								OrderInfo orderInfo = new OrderInfo();
      								orderInfo.setOrderId(IdUtil.fastSimpleUUID());
      								orderInfo.setOrderState("To be paid");
      								orderInfo.setPayMoney(999.88);
      								msgService.sendDelayMsgToMQ(SysConstant.SYS_ORDER_DELAY_EXCHANGE,SysConstant.SYS_ORDER_DELAY_KEY, JSONUtil.toJsonStr(orderInfo),10*1000);//1 minute
      								return JSONUtil.toJsonStr("Sending delay message succeeded");
      						}
      				}
    
  • Start the service, and you can see that the corresponding queues and switches are created in MQ


  • The console log shows that the interval between sending and consuming messages is 10s

7. Get more MQ technical documents

https://www.sunnyblog.top/index.html?tagId=1264009609236971520

Detailed development technical documents Click here to view the technical documents ; more technical articles: https://www.sunnyblog.top ; any questions plus QQ group consultation: 534073451

Topics: Java RabbitMQ