Dead-letter queue

DLX stands for Dead Letter Exchange. The DLX is just a normal switch, no different from a normal switch. When a message becomes a dead message in a queue, the switch sends it to the queue (with the parameters specified, RabbitMQ will send it automatically).

What is a dead letter? What kind of news becomes a dead letter?

A message with TTL set will become a Dead Letter after it expires. There are three forms of message death in RabbitMQ:

  • The message is rejected (basic.reject or basic.nack) and Requeue = false.
  • Message TTL expired
  • The queue reaches its maximum length (the queue is full and no more data can be added to MQ)

Dead letter processing

  • DLX is also a normal Exchange, no different from a normal Exchange, it can be specified on any queue, in effect setting the attributes of a queue.
  • When there is a dead letter in the queue, RabbitMQ will automatically re-publish the message to the Exchange and route it to another queue.
  • Messages in this queue can be listened to and processed accordingly.

Application Scenario analysis:

When defining a service queue, you can specify a dead letter switch and bind a dead letter queue. When a message becomes dead letter, the message will be sent to the dead letter queue, so that we can easily check the cause of the message failure

Specify parameters when defining a business (normal) queue:

  • X-dead-letter-exchange: used to set the exchange for sending dead letters
  • X-dead-letter-routing-key: routingKey used to set dead-letter

Dead letter queue setup

A flowchart


Web Console Operations

1. Declare a dead-letter switch,

Just like a normal switch, it pushes messages to the corresponding queue, but it acts as a switch to forward those dead-letter messages.

The declared Topic switch is called — rabbit_dlx_exchange

2. Declare a dead-letter switch processing queue and bind it.

Here we declare two queues (queue_dlx_A, queue_dlx_B) and bind them to the dead-letter switch using two different routing keys (routing.a.#, routing.b.#).

Binding state

3. Declare a normal working switch, a working switch,

Here we declare a Topic type switch named — rabbit_work_exchange

4. Declare a work queue corresponding to the work switch, bind it to the work switch, and set the routing key to

Declare queue work_queue

Set the queue parameters for queue work_queue

  • X-dead-letter-exchange: Specifies the rabbit_dlx_exchange switch on which dead letters are to be sent
  • X-dead-letter-routing-key: A routingKey used to set a dead letter. If no routingKey is set, the original routing key is used. In this case, the dead-letter routing key is routing.a.key to facilitate the test of the queue to which the route is pushed after being sent to the dead-letter switch.

5. Send a message to the work_queue on the rabbit_work_exchange switch

6, then reject the message in the work_queue

The Web console interface can Reject a message in either of two ways: Nack Message Requeue true (Reject Requeue False). Requeue = true (Reject Requeue false) indicates that the message is returned to the queue.

Results 7.

You can see that messages from work_queue are forwarded to queue_dlx_A through the dead-letter switch.

Code way

Declare switches, queues
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
public class Config {

    // Declare dead letter exchange rabbit_dlx_exchange
    @Bean
    public TopicExchange dlxExchange(a) {
        return new TopicExchange("rabbit_dlx_exchange");
    }
    @Bean
    public Queue dlxQueueA(a) {
        return new Queue("queue_dlx_A");
    }
    @Bean
    public Queue dlxQueueB(a) {
        return new Queue("queue_dlx_B");
    }
    @Bean
    public Binding bindDlxAQueue(a) {
        String routingKey = "routing.a.#";
        return BindingBuilder.bind(dlxQueueA()).to(dlxExchange()).with(routingKey);
    }
    @Bean
    public Binding bindDlxBQueue(a) {
        String routingKey = "routing.b.#";
        return BindingBuilder.bind(dlxQueueB()).to(dlxExchange()).with(routingKey);
    }

    // Declare a working switch rabbit_work_exchange
    @Bean
    public TopicExchange workExchange(a) {
        return new TopicExchange("rabbit_work_exchange");
    }

    @Bean
    public Queue workQueue(a) {
        String queueName = "work_queue";
        // The dead-letter switch to specify
        String deadExchangeName = "rabbit_dlx_exchange";
        // The routing key is simulated to the A queue of the dead letter switch
        String deadRoutingKey = "routing.a.key";
        Map<String, Object> args = new HashMap<>(2);
        args.put("x-dead-letter-exchange", deadExchangeName);
        args.put("x-dead-letter-routing-key", deadRoutingKey);
        return new Queue(queueName, true.false.false, args);
    }
    @Bean
    public Binding bindWorkQueue(a) {
        String routingKey = "#";
        returnBindingBuilder.bind(workQueue()).to(workExchange()).with(routingKey); }}Copy the code

The Map<String,Object> parameter is injected into the normal work queue work_queue

X-dead-letter-exchange Identifies a switch

X-dead-letter-routing-key identifies a binding key.

consumers
@Component
public class Consumer {

    @RabbitListener(queues = "work_queue")
    public void receiver(String msg, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {
        System.out.println("Work_queue consumption information:" + msg);
        System.out.println("Put the message rejection back into the queue");
        channel.basicReject(deliveryTag,  false);
        // Either method is acceptable, but requeue must be false
// channel.basicNack(deliveryTag, false, false);
    }

    @RabbitListener(queues = "queue_dlx_A")
    public void receiver2(String msg, Channel channel) throws IOException {
        System.out.println(Queue_dlx_A on the dead-letter switch received a message: + msg);
        channel.basicAck(1.false);
    }
    @RabbitListener(queues = "queue_dlx_B")
    public void receiver3(String msg, Channel channel) throws IOException {
        System.out.println(Queue_dlx_B queue on dead letter switch received message: + msg);
        channel.basicAck(1.false); }}Copy the code
producers
@Test
public void producer(a) {
	String exchange = "rabbit_work_exchange";
	String routingKey = "a";
	String msg = "Simulate a dead-letter message.";
	rabbitTemplate.convertAndSend(exchange, routingKey, msg);
}
Copy the code
The output
Received a message from queue work_queue: emulated a dead letter message Consumer rejected a dead letter Message Received a message from queue DLX_A on the switch: Emulated a dead letter messageCopy the code

Dead letter messages are a layer of assurance provided by RabbitMQ that the Exchange and Queue can work together as much as they like, instead of using a dead letter Queue, they can be actively sent to another switch in the event of an abnormal message consumption.

For example, pull messages from a dead letter queue, and then send emails, SMS, etc., for asynchronous processing logic.

Or repost the message to a queue and set an expiration time for delayed consumption.