Distributed topic|Because I don’t know how Rabbit implements the delay queue, I finally failed to enter the big factory


Students who have studied rabbitmq should all know that rabbitmq does not have a delay queue function. Why does the interviewer still ask this weird question?
Because the interviewer asks you this question to test your logical ability to integrate knowledge.

I can say with certainty here: rabbitmq does not implement the function of delay queue, but we can save the country by curve, using dead letter queue + TTL can also realize the function of delay queue.

There is another way to implement it through the delay queue plugin, which I will introduce later.

Delay queue usage scenarios

The most used place is the order payment overtime to cancel the order

Before talking about how to implement it, let's first introduce what is dead letter queue and TTL:

Key points explained

Dead letter queue

In rabbitmq, the dead letter queue should actually be called a dead letter switch, so what does this dead letter mean?
There is no difference between a dead letter queue and a normal queue, but users will not actively send messages to this queue or switch. Only when the following situations occur, messages will be forwarded from the original queue to the dead letter queue:

  • The message length of the original queue exceeds the predetermined limit
  • The consumer rejects the message, basicNack/basicReject, and does not put the message back in the queue
  • The original queue message has an expiration time. If it has not been consumed by the consumer before it expires, it will also be transferred to the dead letter queue;

The related setting parameters of the dead letter queue are set in the queue: x-dead-letter-exchange ,
x-dead-letter-routing-key
Insert picture description here

TTL

The full name of TTL is Time To Live, which translates to expiration time. When a message has survived to the time set by ttl and has not been consumed, the message will be cleared. Rabbit can set expired messages on the queue or set expired messages on specific messages. , Here is a small interview question:

Q: How does rabbit handle messages with an expiration time set?

Answer: Rabbit implements a lazy strategy to clean up the expiration time, the purpose is to ensure the high throughput of the message queue; this lazy strategy is that after the message reaches the top of the queue, the broker will check whether the queue has an expiration time set, if If it is set, it will check whether the expiration time has arrived. If it is, the message will be discarded and the message will not be pushed. Remember not to answer. The broker will traverse each message and check the expiration time. Remember! ! !

Two important technical points have been introduced before, and now it is time to enter the topic of this article. How does rabbitmq implement the delay queue?

Use TTL+DLX

Realization idea

Presumably, after my explanation on TTL and dead letter queues, you may already know how to implement it, but even if you know how to implement it, I still have to talk about it, haha

Because TTL can set the expiration time for the message, and there is one of the conditions for entering the dead letter queue: the original queue message has an expiration time set, if it has not been consumed by consumers before it expires, it will also be transferred to the dead letter In the queue , then we can combine the two to do this. The listener handling the normal business listens to the dead letter queue, and then sets the parameters of the dead letter queue to the normal queue, then the message flow will become like this:

  • I sent a message with an expiration time of 10000 milliseconds to the broker
  • The broker puts the message in the queue
  • After 10000 milliseconds, the message has not been consumed
  • The broker will forward the message to the dead letter exchange, and then the dead letter exchange will push the message to the dead letter queue
  • I have just set up a listener to monitor the dead letter queue, so I must have received this message after 10000 milliseconds;

Code writing

  • Producer queue and exchange binding and queue declaration
@Configuration
public class RabbitMQConfig {
    
    
    public static final String QUEUE_TEST_DLX = "queue_test_dlx";
    public static final String QUEUE_TEST_NORMAL = "queue_test_normal";
    public static final String EXCHANGE_TEST_DLX = "exchange_test_dlx";
//    声明一个默认不进行消费的队列 绑定死信队列交换机和死信队列的key
    @Bean("queueTestNormal")
    public Queue queueTestNormal() {
    
    
        return QueueBuilder.durable(QUEUE_TEST_NORMAL).deadLetterExchange(EXCHANGE_TEST_DLX).deadLetterRoutingKey("testdlx").build();
    }
    //    声明死信队列
    @Bean("queueTestDLX")
    public Queue queueTestDLX() {
    
    
        return QueueBuilder.durable(QUEUE_TEST_DLX).build();
    }
    //  声明死信交换机
    @Bean("exchangeTestDLX")
    public Exchange exchangeTestDLX() {
    
    
        return ExchangeBuilder.directExchange(EXCHANGE_TEST_DLX).durable(true).build();
    }
    //    死信队列与死信交换机绑定
    @Bean
    public Binding itemQueueExchange7(@Qualifier("queueTestDLX") Queue queue,
                                      @Qualifier("exchangeTestDLX") Exchange exchange) {
    
    
        return BindingBuilder.bind(queue).to(exchange).with("testdlx").noargs();
    }
    }
  • The producer simply sends a message to the ordinary queue and sets the expiration time to 10s
    @Test
    public void testDLX() {
    
    
        rabbitTemplate.convertAndSend(null, "queue_test_normal","我是10秒之后才到的", new MessagePostProcessor() {
    
    
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
    
    
                MessageProperties messageProperties = message.getMessageProperties();
                messageProperties.setExpiration(10000+"");
                return message;
            }
        });
        System.out.println("我发送消息的时间为:"+(System.currentTimeMillis()));
        System.out.println("开始倒计时:10");
        int i = 10;
        while (true){
    
    
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            if(i>0){
    
    
                System.out.println("倒计时:"+(--i));
            }

        }
    }
  • Consumer listener writing
    @RabbitListener(queues = "queue_test_dlx")
    public void onMessage5(Message message, Channel channel) throws Exception {
    
    
        System.out.println("我收到消息的时间为:"+(System.currentTimeMillis()));
        System.out.println("收到消息来自队列queue_test_dlx:" + new String(message.getBody()));
    }

to sum up

so far. The delay queue has been implemented, we will now summarize the only shortcomings of this way to achieve the delay queue:

Not in time : Because only when the message reaches the top of the queue, the broker will check whether the message has expired and push it, adding a message with a longer expiration time before the message with the expiration time set, which will cause the expiration time to be small The message has not been processed and has been waiting in the queue;

For this reason, rabbitmq introduced a delay queue plug-in. The implementation idea of ​​this plug-in is different from the previous implementation. When a delay time is set for a message, it will not immediately push the message to the queue, but wait for the message. It is put in the queue after the set delay time. We now introduce how the delay queue plugin is implemented:

Use delay queue plugin

Install delay queue plugin

#下载插件 https://www.cnblogs.com/geekdc/p/13549613.html

docker cp /Users/yangle/docker/rabbitmq/plugins/rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez rabbitmq:/plugins

#进入容器

docker exec -it rabbitmq /bin/bash

#启用插件

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

#查看

rabbitmq-plugins list

#重新启动容器

docker restart rabbitmq

Code writing

  • Switch and queue binding configuration file
@Configuration
public class RabbitMQConfig {
    
    
    public static final String QUEUE_TEST_DELAY_PLUGIN = "queue_test_delay_plugin";
    public static final String EXCHANGE_TEST_DELAY_PLUGIN = "exchange_test_delay_plugin";
     //    声明一个队列
    @Bean("queueDelayPlugin")
    public Queue queueDelayPlugin() {
    
    
        return QueueBuilder.durable(QUEUE_TEST_DELAY_PLUGIN).build();
    }
    @Bean
    CustomExchange delayExchange(){
    
    
        Map<String, Object> args = new HashMap<>();
        // 设置为路由模式
        args.put("x-delayed-type", "direct");
        // type必须设置为x-delayed-message
        return new CustomExchange(EXCHANGE_TEST_DELAY_PLUGIN, "x-delayed-message", true,false, args);
    }

    //    插件交换机与队列绑定
    @Bean
    public Binding itemQueueExchange8(@Qualifier("queueDelayPlugin") Queue queue,
                                      @Qualifier("delayExchange") Exchange exchange) {
    
    
        return BindingBuilder.bind(queue).to(exchange).with("testDelayPlugin").noargs();
    }}
  • send messages
    @Test
    public void testDelayPlugin() {
    
    
        rabbitTemplate.convertAndSend("exchange_test_delay_plugin", "testDelayPlugin", "测试延时插件发送消息", new MessagePostProcessor() {
    
    
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
    
    
                message.getMessageProperties().setDelay(10000);
                return message;
            }
        });
    }
  • Listener
    @RabbitListener(queues = "queue_test_delay_plugin")
    public void onMessage6(Message message, Channel channel) throws Exception {
    
    
        System.out.println("我收到消息的时间为:"+(System.currentTimeMillis()));
        System.out.println("收到消息来自队列queue_test_delay_plugin:" + new String(message.getBody()));
    }

to sum up

Although the plug-in implements the delay queue is simple, it also has its limitations:

  • Will reduce performance, so if there is no such demand, don't use it.
  • This plug-in is not suitable for delayed messages with large amounts of data, such as one million or one hundred million.
  • Delay time: 0<=n<=(2^32)-1, in milliseconds.

Wechat search for a search [Le Zai open talk] Follow the handsome me, reply [Receive dry goods], there will be a lot of interview materials and architect must-read books waiting for you to choose, including java basics, java concurrency, microservices, middleware, etc. More information is waiting for you.

Guess you like

Origin blog.csdn.net/weixin_34311210/article/details/109701943