Daily record - SpringBoot integrates RabbitMQ Section 5 (dead letter)

1. Dead letter

1. What is dead letter

When a message in a queue meets one of the following conditions, it can become a dead letter:

  1. Message TTL expired;
  2. The queue reaches the maximum length (the queue is full, no more data can be added to RabbitMQ);
  3. The message is rejected (basic.reject or basic.nack) and the message sets requeue=false (does not re-enqueue);

By default, dead letters are discarded directly
insert image description here

2. Consumption failure becomes a dead letter

Retry policy:

  1. RejectAndDontRequeueRecoverer: After the retries are exhausted, reject directly and lose the message. is the default processing strategy
  2. ImmediateRequeueMessageRecoverer: After retries are exhausted, nack is returned, and the message is re-queued
  3. RepublishMessageRecoverer: After the retries are exhausted, deliver the failure message to the specified exchange

In the failure retry strategy, the default RejectAndDontRequeueRecoverer will send reject to RabbitMQ after the number of local retries is exhausted, and the message becomes a dead letter and is discarded. (If you use the RepublishMessageRecoverer strategy, remember to turn it off, otherwise the message may be thrown into the queue bound to the strategy)

We can add a dead letter switch to the queue and bind a dead letter queue to the dead letter switch. In this way, after the message becomes a dead letter, it will not be discarded, but will be finally delivered to the dead letter switch and routed to the dead letter queue bound to the dead letter switch.

3. Define dead letter switches and dead letter queues

Dead letter switches and dead letter queues are also common switches and queues, so they are defined as follows:

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


//死信交换机和死信队列也是普通的交换机和队列
@Configuration
public class DeadQueueConfig {
    
    

    //定义死信交换机
    @Bean
    public DirectExchange deadLetterExchange() {
    
    
        return ExchangeBuilder.directExchange("dead.letter.exchange").build();
    }


    //定义死信队列
    @Bean
    public Queue deadLetterQueue() {
    
    
        return new Queue("dead.letter.queue",true,false,false);
    }

    //交换机和队列绑定
    @Bean
    public Binding deadLetterQueueBinding(Queue deadLetterQueue, DirectExchange deadLetterExchange) {
    
    
        return BindingBuilder.bind(deadLetterQueue).to(deadLetterExchange).with("dl");
    }
}

4. How to bind dead letter switches and queues

Bind dead letter exchange and dead letter queue

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

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


@Configuration
public class TestDeadQueueConfig {
    
    

    @Bean
    public DirectExchange testDeadLetterExchangeExchange() {
    
    
        return ExchangeBuilder.directExchange("test.dead.letter.exchange").build();
    }



    @Bean
    public Queue testDeadLetterQueue() {
    
    


     return QueueBuilder.durable("test.dead.letter.queue")
             //test.dead.letter.queue队列绑定死信交换机
             .deadLetterExchange("dead.letter.exchange")
             //死信交换机和死信队列绑定 routingKey = dl
             .deadLetterRoutingKey("dl")
             .build();

        /*Map<String, Object> args = new HashMap();
        // DLX(死信交换机)
        args.put("x-dead-letter-exchange", "死信队列交换机的名称");
        // DLK(死信路由key)
        args.put("x-dead-letter-routing-key", "死信消息路由的routingKey");
        // TTL(time-to-live存活时间)
        args.put("x-message-ttl", 10000);
        return new Queue("test.dead.letter.queue",true,false,false,args);*/

    }

    //交换机和队列绑定
    @Bean
    public Binding testDeadLetterQueueBinding(Queue testDeadLetterQueue, DirectExchange testDeadLetterExchangeExchange) {
    
    
        return BindingBuilder.bind(testDeadLetterQueue).to(testDeadLetterExchangeExchange).with("test.dead.letter");
    }
}

insert image description here

5. After the message fails, it is delivered to the dead letter

mock producer

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class DeadQueueTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void test(){
    
    
       //模拟生产者
       rabbitTemplate.convertAndSend("test.dead.letter.exchange","test.dead.letter","进行死信测试");


    }
}

mock consumer


import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class DeadQueueListener {
    
    

    @RabbitListener(queues = "test.dead.letter.queue")
    public void testDeadLetterQueueMsg(String msg){
    
    

        System.out.println("test.dead.letter.queue收到消息:"+msg);

        //模拟:处理消息中出现了异常
        int i = 1/0;

        System.out.println("消息处理完毕!");
    }
}

insert image description here
insert image description here
The message was successfully delivered to the dead letter queue

6. The message is timed out and delivered to the dead letter

By default, the message will not expire. If you do not set any parameters related to message expiration, the message will not expire. Even if the message is not consumed, it will always be stored in the queue.

If TTL is set, the message will be automatically deleted after this time is exceeded.

  1. Set by queue properties: all messages in the queue have the same expiration time
  2. Set the message individually: each message TTL can be different
  3. Use both methods at the same time, the expiration time is subject to the smallest value.

1. Demo queue timeout TTL, delivered to the dead letter queue

Define a queue and set an expiration time for it

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

@Configuration
public class TTLQueueConfig {
    
    


    @Bean
    public DirectExchange ttlExchange(){
    
    
        return ExchangeBuilder.directExchange("ttl.exchange").build();
    }

    //定时队列,设置过期时间,并且绑定死信交换机
    @Bean
    public Queue ttlQueue(){
    
    
        return QueueBuilder.durable("ttl.queue")
                //设置队列的超时时间为10s
                .ttl(10*1000)
                //给队列设置死信交换机,名称为dead.letter.exchange 设置投递死信时的RoutingKey为dl
                .deadLetterExchange("dead.letter.exchange")
                .deadLetterRoutingKey("dl")
                .build();
    }

    @Bean
    public Binding ttlBinding(Queue ttlQueue, DirectExchange ttlExchange){
    
    
        return BindingBuilder.bind(ttlQueue).to(ttlExchange).with("test.ttl.queue");
    }


}

mock producer

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class TTLQueueTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //模拟生产者
    @Test
    public void test(){
    
    
        rabbitTemplate.convertAndSend("ttl.exchange","test.ttl.queue","这个队列10秒后过期,然后到死信队列");
    }


}

After starting the service
insert image description here
calls the producer
insert image description here
insert image description here

2. Demonstration message timeout TTL, delivered to the dead letter queue

Queue and dead-letter exchange with test queue timeout

import org.junit.jupiter.api.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalTime;

@SpringBootTest
public class TTLQueueTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //模拟生产者
    @Test
    public void testTTLMessage() {
    
    
        String msgStr = "消息TTL demo,发送时间是:" + LocalTime.now();

        Message message = MessageBuilder
                .withBody(msgStr.getBytes())
                //设置消息TTL为5秒
                .setExpiration("5000")
                .build();

        //发送消息时:
        //  如果消息和队列都设置了TTL,则哪个TTL短,哪个生效
        //  如果消息和队列只设置了一个TTL,则直接以设置的为准
        rabbitTemplate.convertAndSend("ttl.exchange", "test.ttl.queue", message);
    }



}

Usage scenario: The user places an order, and if the user does not pay within 15 minutes, the order is automatically canceled and so on.

Guess you like

Origin blog.csdn.net/qq407995680/article/details/132142406