RabbitMQ dead letter queue

Posted by dean7 on Sat, 15 Jan 2022 16:47:58 +0100

Definition: dead letter refers to messages that cannot be consumed. Dead letter queue is the queue for storing dead letters
Source: Message TTL expired, maximum queue length reached, message rejected

actual combat

One producer and two consumers (C1 and C2), and the producer passes normal of direct type_ Exchange (the bindingKey is zhangsan) publishes the message to the normal queue. C1 gets the message from the queue and consumes it normally. If the message in the normal queue is abnormal, it becomes a dead letter and passes through dead_exchange is sent to dead queue and processed by C2

Simulate ttl expiration

Turn on the producer but not the consumer, so that the messages released by the producer cannot be consumed. When the ttl expires, they will be sent to the dead letter queue through the dead letter switch

Consumer C1

Close immediately after startup (startup is to declare the common switch, common queue, dead letter switch and dead letter queue, bind the common queue and common switch (bind through bindingkey), dead letter queue and dead letter switch; Closing is to simulate that there is no consumer consumption message, and the expired message is sent to the dead letter queue)

package com.lin.rabbitmq.eight;


import com.lin.rabbitmq.utils.RabbitMqUtils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

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

//Consumer C1
public class Consumer1 {

     public static final String NORMAL_EXCHANGE = "normal_exchange";
     public static final String DEAD_EXCHANGE = "dead_exchange";
     public static final String NORMAL_QUEUE = "normal_queue";
     public static final String DEAD_QUEUE = "dead_queue";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();

        //Declare dead letter and general switch
        channel.exchangeDeclare(NORMAL_EXCHANGE,BuiltinExchangeType.DIRECT);
        channel.exchangeDeclare(DEAD_EXCHANGE,BuiltinExchangeType.DIRECT);

        //Declare normal queue
         Map<String,Object> arguments =new HashMap<>( );
         //Expiration time
         // arguments.put("x-message-ttl",10000);
         //Normal queue setting dead letter switch
         arguments.put("x-dead-letter-exchange",DEAD_EXCHANGE);
         //Set dead letter RoutingKey
         arguments.put("x-dead-letter-routing-key","lisi");
        channel.queueDeclare(NORMAL_QUEUE,false,false,false,arguments);

        //Declare dead signal queue
        channel.queueDeclare(DEAD_QUEUE,false,false,false,null);

        //Binding common queue and common switch
        channel.queueBind(NORMAL_QUEUE,NORMAL_EXCHANGE,"zhangsan");   //bindingKey "zhangsan"
        //Bind dead letter queue and dead letter switch
        channel.queueBind(DEAD_QUEUE,DEAD_EXCHANGE,"lisi");


        System.out.println("Waiting to receive message:");
        DeliverCallback deliverCallback = (consumerTag,message)->{
            System.out.println("Consumer1 The message received is:"+new String(message.getBody()));
        };
        channel.basicConsume(NORMAL_QUEUE,true,deliverCallback,(consumerTag)->{});
    }
}

Producer

package com.lin.rabbitmq.eight;


import com.lin.rabbitmq.utils.RabbitMqUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;

//producer
public class Producer {

        public static final String NORMAL_EXCHANGE = "normal_exchange";

    public static void main(String[] args) throws Exception {

        Channel channel = RabbitMqUtils.getChannel();

        //Setting TTL time for dead letter message
        AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("10000").build();

        for(int i=1;i<=10;i++){
            String message = "info"+i;
            channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",properties,message.getBytes());
        }
    }
}

Result display

normal_ In the Features column of the queue, ttl is the ttl expiration time we set in the Producer. DLX and DLK are abbreviations of x-dead-letter-exchange and x-dead-letter-routing-key

Consumer C2

Consumer C2 is used to consume messages in the dead letter queue, and the results are not displayed

package com.lin.rabbitmq.eight;


import com.lin.rabbitmq.utils.RabbitMqUtils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

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

//Consumer C2
public class Consumer2 {


     public static final String DEAD_QUEUE = "dead_queue";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();

        System.out.println("Waiting to receive message:");
        DeliverCallback deliverCallback = (consumerTag,message)->{
            System.out.println("Consumer2 The message received is:"+new String(message.getBody()));
        };
        channel.basicConsume(DEAD_QUEUE,true,deliverCallback,(consumerTag)->{});
    }
}

The queue has reached its maximum length

First, remove the TTL attribute from the original producer code, and add the x-max-length attribute to consumer C1 and arguments

arguments.put("x-max-length",6);

Then delete the original normal in the rabbitmq web management interface_ Queue (the parameter is changed to avoid conflict), and then start consumer C1 as usual and close it immediately. It is found that

The producer sent 10 messages, but there was no consumer consumption because of normal_ The length of the queue is 6, so the remaining 4 messages are sent to the dead letter queue (no ttl time is set, so the messages in the normal_queue will not expire)

After that, open consumer C2 to consume the messages in the dead letter queue. The results are as follows. You can see that all messages in the dead letter queue have been consumed

Message rejected

First, comment out the argument of consumer C1 as usual Put ("x-max-length", 6), delete the common queue in the web management interface, and modify the consumption callback function of C1, as follows

    DeliverCallback deliverCallback = (consumerTag,message)->{
            String msg = new String(message.getBody());
            if(msg.equals("info5")){
                System.out.println("Consumer1 The message received is:"+msg+":This message was C1 Rejected");
                channel.basicReject(message.getEnvelope().getDeliveryTag(),false);
            }else{
                System.out.println("Consumer1 The message received is:"+msg);
                //Response message, false, do not start batch response
                channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
            }
        };

Then start the manual response (the second parameter of the basicConsume method is changed to false)
Automatic response: after receiving the message, the consumer returns a response without processing it (calling the consumption callback function)
Manual response: the response is returned only after the processing is completed. You can respond in batches (multiple)

 //Turn on manual answer
        channel.basicConsume(NORMAL_QUEUE,false,deliverCallback,(consumerTag)->{});

Result display

Only the rejected message 5 is sent to the dead letter queue, and then the message in the dead letter queue can be processed through consumer C2

Topics: Java RabbitMQ queue Middleware switch