The use of RabbitMQ delayed queue

Table of contents

1. Delay queue usage scenarios

2. Delay queue implementation idea in RabbitMQ

3. Implementation example

3. Run project tests


1. Delay queue usage scenarios

Delay queues can generally be used for time-limited tasks, such as time-limited offers, overtime order processing, etc.

For this scenario, the traditional processing method is task polling: through a background task, the order information is continuously scanned, and if there is an overtime order, it will be processed. The advantage of this processing method is that the implementation idea is simple and easy to grasp. The disadvantage is that the server And the pressure of the data is relatively high (because usually a large amount of data needs to be scanned).

The second way to handle this scenario is through delay queues. After the message producer generates the message and puts it in the queue, the message consumer can consume the message after the specified delay time.

2. Delay queue implementation idea in RabbitMQ

Delay queues are not directly supported in RabbitMQ, and no corresponding properties can be set. The basic idea of ​​implementing delay queues in RabbitMQ is to implement delay queues through dead letter queues (DXL) and expiration time (TTL).

That is: set an expiration time for the queue and specify a dead letter switch to associate with it. The message producer sends the message to the queue, but does not specify the message consumer. Wait for the message to expire. After the message expires, it will be forwarded to the associated dead letter queue. , while the message consumer consumes messages from the dead letter queue.

3. Implementation example

general idea:

  1. Declare a dead-letter exchange, a queue, and bind the queue to the dead-letter exchange.
  2. Declare the exchange and queue for sending messages (set the expiration time of the queue according to business requirements, but the queue does not require message consumers), and associate the queue with the exchange.
  3. Write business code to send messages to the queue through the exchange created in step 2. (Dump the expired message to the dead letter queue after observing the message expiration)
  4. Write a message consumer to consume messages in the dead letter queue. (In the actual project, the message consumer is the handler for the delayed task)

Specific steps

Preparation

First configure the specific steps of the RabbitMQ server

YouDao Cloud Note

springboot version 2.7.7

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit-test</artifactId>
    <scope>test</scope>
</dependency>

application.properties configuration file


server.port=8081
## rabbitmq config
spring.rabbitmq.host=192.168.164.128
spring.rabbitmq.port=5672
spring.rabbitmq.username=xhz
spring.rabbitmq.password=123
spring.rabbitmq.virtual-host=my_vhost
## 消费者数量
spring.rabbitmq.listener.simple.concurrency=10
spring.rabbitmq.listener.simple.max-concurrency=10
#消费者每次从队列中获取的消息数量
spring.rabbitmq.listener.simple.prefetch=1
#消费者自动启动
spring.rabbitmq.listener.simple.auto-startup=true
#消费失败,自动重新入队
spring.rabbitmq.listener.simple.default-requeue-rejected=true
#启用发送重试
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=1000
spring.rabbitmq.template.retry.max-attempts=3
spring.rabbitmq.template.retry.max-interval=10000
spring.rabbitmq.template.retry.multiplier=1.0

1) Declare a dead-letter exchange, a queue, and bind the queue to the dead-letter exchange.

Perform switch (eg: RabbitMQConfig), queue declaration, and binding operations in the @Configuration class.

package com.rabbitmq.provider.rabbitmqprovider.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;

@Configuration
public class RabbitDLXConfig {
    public final static String NORMAL_QUEUE="normal_queue";
    public final static String NORMAL_ROUTING_KEY = "normal_routing_key";
    public final static String NORMAL_EXCHANGE = "normal_exchange";

    public final static String DELAY_QUEUE = "delay_queue";
    public final static String DELAY_ROUTING_KEY = "delay_routing_key";
    public final static String DELAY_EXCHANGE = "delay_exchange";

    //写法一
    //    普通交换机以及普通队列
    @Bean
    public Queue normalQueue(){
        Map map = new HashMap();
        map.put("x-message-ttl", 20000);//message在该队列queue的存活时间最大为10秒
        map.put("x-dead-letter-exchange", DELAY_EXCHANGE); //x-dead-letter-exchange参数是设置该队列的死信交换器(DLX)
        map.put("x-dead-letter-routing-key", DELAY_ROUTING_KEY);//x-dead-letter-routing-key参数是给这个DLX指定路由键

        return new Queue(NORMAL_QUEUE, true, false, false, map);
    }
  //写法二
    /**
     * 声明队列,用于实现延迟队列,该队列指定超时时间
     * @return
     */
 /*   @Bean(name="normalQueue")
    public Queue normalQueue() {
        return QueueBuilder
                .durable(NORMAL_QUEUE)
                .withArgument("x-message-ttl", 1000*60*1)
                .withArgument("x-dead-letter-exchange", DELAY_EXCHANGE)
                .withArgument("x-dead-letter-routing-key",DELAY_ROUTING_KEY)
                .build();
    }*/

    @Bean
    public DirectExchange normalExchange(){
        return new DirectExchange(NORMAL_EXCHANGE, true, false);
    }

    @Bean
    public Binding normalBinding(@Qualifier("normalExchange") DirectExchange exchange,
                                 @Qualifier("normalQueue") Queue queue){
        return BindingBuilder.bind(queue)
                .to(exchange)
                .with(NORMAL_ROUTING_KEY);
    }

    //    死信交换机及延迟队列
    @Bean
    public Queue delayQueue(){
        return new Queue(DELAY_QUEUE);
    }

    @Bean
    public DirectExchange delayExchange(){
        return new DirectExchange(DELAY_EXCHANGE);
    }

    @Bean
    public Binding delayBinding(){
        return BindingBuilder.bind(delayQueue())
                .to(delayExchange())
                .with(DELAY_ROUTING_KEY);
    }

}

2) Write the program code for sending messages

package com.rabbitmq.provider.rabbitmqprovider.web;

import com.rabbitmq.provider.rabbitmqprovider.config.RabbitDLXConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

@RestController
public class SenderController {

    @Autowired
    private RabbitTemplate rabbitTemplate;
  
    @RequestMapping("sendDLX")
    public Map sendDLX(){
        Map msg = new HashMap();
        msg.put("msg","这是通过死信交换机投递的消息");
        msg.put("now", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        rabbitTemplate.convertAndSend(RabbitDLXConfig.NORMAL_EXCHANGE,RabbitDLXConfig.NORMAL_ROUTING_KEY,msg);

        Map res = new HashMap();
        res.put("msg","投递成功");
        res.put("code",200);
        return res;
    }

}

3. Run project tests

Access address: http://localhost:8081/sendDirect

Verification: The sent message will be sent to the "delay.queue" queue first, and after the message expires, the message will be sent to the "delay.dxl.queue" (dead letter queue).

Guess you like

Origin blog.csdn.net/qq_62898618/article/details/128513600