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.