تبادلات الرسائل الميتة (DLX)
هيّئ تبادلات الرسائل الميتة للتعامل مع الرسائل التي يتعذر توصيلها أو معالجتها بنجاح. ونفّذ استراتيجيات قوية لمعالجة الأخطاء وإعادة معالجة الرسائل.
تبادلات الرسائل الميتة (DLX) درس مجاني في RabbitMQ Messaging & Async Systems على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في RabbitMQ Messaging & Async Systems، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة RabbitMQ Messaging & Async Systems 4 دروس في المجموع.
بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.
What is a Dead Letter Exchange?
In message queuing, sometimes messages can't be processed successfully. They might be invalid, or the consumer might fail. What happens to these 'problem' messages?
A Dead Letter Exchange (DLX) in RabbitMQ is a mechanism for handling messages that cannot be delivered or processed. It's like a special mailbox for 'undeliverable' mail.
When Messages Go Astray
Messages are 'dead-lettered' (sent to a DLX) under specific conditions:
- Rejected by Consumer: A consumer explicitly rejects a message (using
basic.rejectorbasic.nack) and setsrequeuetofalse. - Message TTL Expiration: A message's Time-To-Live (TTL) expires while it's in a queue.
- Queue Length Limit: The queue reaches its maximum length, and new messages cause older ones to be dropped.
- Message Not Routable: If a message is published to an exchange with a mandatory flag, but it cannot be routed to any queue, it can also be dead-lettered. (Less common, but possible)
Basic DLX Configuration
To use a DLX, you need to configure your main queue to point to it. This is done when you declare the main queue:
x-dead-letter-exchange: The name of the exchange to which dead-lettered messages will be sent.x-dead-letter-routing-key: An optional routing key to use when publishing to the DLX. If not set, the original routing key of the dead-lettered message is used.
You also need to declare the DLX itself and a 'dead-letter queue' (DLQ) that's bound to the DLX.
DLX Setup & Producer (Nack Example)
Let's set up a main queue that sends rejected messages to a DLX. This producer declares a DLX, a dead-letter queue (DLQ), and binds them. Then, it declares our main_queue, configuring it to use the DLX.
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class DLXProducerNack {
private static final String MAIN_QUEUE_NAME = "main_nack_queue";
private static final String DLX_EXCHANGE_NAME = "dlx_exchange";
private static final String DLQ_QUEUE_NAME = "dlq_nack_queue";
private static final String DLQ_ROUTING_KEY = "dlq_nack_key";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// 1. Declare DLX and DLQ
channel.exchangeDeclare(DLX_EXCHANGE_NAME, "topic", true);
channel.queueDeclare(DLQ_QUEUE_NAME, true, false, false, null);
channel.queueBind(DLQ_QUEUE_NAME, DLX_EXCHANGE_NAME, DLQ_ROUTING_KEY);
// 2. Declare main queue with DLX arguments
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", DLX_EXCHANGE_NAME);
args.put("x-dead-letter-routing-key", DLQ_ROUTING_KEY);
channel.queueDeclare(MAIN_QUEUE_NAME, true, false, false, args);
String message = "Message to be rejected!";
channel.basicPublish("", MAIN_QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
System.out.println(" [x] Sent '" + message + "' to " + MAIN_QUEUE_NAME);
}
}
}Consumer with Basic Reject
This consumer listens to the main_nack_queue. When it receives a message, it uses basicReject(deliveryTag, false) to reject it without requeueing. This sends the message to our configured DLX. The consumer also listens to the DLQ to show the dead-lettered message.
Run the producer from the previous scene, then run this consumer.
import com.rabbitmq.client.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class DLXConsumerNack {
private static final String MAIN_QUEUE_NAME = "main_nack_queue";
private static final String DLQ_QUEUE_NAME = "dlq_nack_queue";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// Consumer for the main queue, will reject messages
System.out.println(" [*] Waiting for messages in " + MAIN_QUEUE_NAME + ".");
DeliverCallback mainQueueDeliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
System.out.println(" [x] Received from " + MAIN_QUEUE_NAME + ": '" + message + "'");
System.out.println(" [x] Rejecting message. It should go to DLQ.");
channel.basicReject(delivery.getEnvelope().getDeliveryTag(), false); // Don't requeue
};
channel.basicConsume(MAIN_QUEUE_NAME, false, mainQueueDeliverCallback, consumerTag -> {});
// Consumer for the dead-letter queue
System.out.println(" [*] Waiting for messages in " + DLQ_QUEUE_NAME + " (DLQ).");
DeliverCallback dlqDeliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
System.out.println(" [DLQ] Received dead-lettered message: '" + message + "'");
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
};
channel.basicConsume(DLQ_QUEUE_NAME, false, dlqDeliverCallback, consumerTag -> {});
}
}DLX with Message TTL (Producer)
Messages can also be dead-lettered if they expire. This producer sends a message with a short Time-To-Live (TTL) of 5 seconds to a main queue. If no consumer processes it within that time, it will be dead-lettered.
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.AMQP;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class DLXTTLProducer {
private static final String MAIN_QUEUE_NAME = "main_ttl_queue";
private static final String DLX_EXCHANGE_NAME = "dlx_exchange"; // Reuse DLX
private static final String DLQ_QUEUE_NAME = "dlq_ttl_queue"; // Separate DLQ for TTL
private static final String DLQ_ROUTING_KEY = "dlq_ttl_key";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// Declare DLX and DLQ (if not already done)
channel.exchangeDeclare(DLX_EXCHANGE_NAME, "topic", true);
channel.queueDeclare(DLQ_QUEUE_NAME, true, false, false, null);
channel.queueBind(DLQ_QUEUE_NAME, DLX_EXCHANGE_NAME, DLQ_ROUTING_KEY);
// Declare main queue with DLX arguments and message TTL
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", DLX_EXCHANGE_NAME);
args.put("x-dead-letter-routing-key", DLQ_ROUTING_KEY);
args.put("x-message-ttl", 5000); // Message TTL of 5 seconds
channel.queueDeclare(MAIN_QUEUE_NAME, true, false, false, args);
String message = "This message will expire!";
channel.basicPublish("", MAIN_QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
System.out.println(" [x] Sent '" + message + "' to " + MAIN_QUEUE_NAME);
System.out.println(" [x] Message has a TTL of 5 seconds. It will dead-letter if not consumed.");
}
}
}Observing TTL Dead-Lettering (Consumer)
This consumer only listens to the dead-letter queue (dlq_ttl_queue). Run the producer from the previous scene, then run this consumer. You will see the message appear in the DLQ after its 5-second TTL expires, even if no consumer rejects it.
import com.rabbitmq.client.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class DLXTTLConsumer {
private static final String DLQ_QUEUE_NAME = "dlq_ttl_queue";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// Consumer for the dead-letter queue for TTL messages
channel.queueDeclare(DLQ_QUEUE_NAME, true, false, false, null);
System.out.println(" [*] Waiting for dead-lettered messages in " + DLQ_QUEUE_NAME + ".");
System.out.println(" Run DLXTTLProducer first, then wait 5 seconds.");
DeliverCallback dlqDeliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
System.out.println(" [DLQ] Received expired dead-lettered message: '" + message + "'");
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
};
channel.basicConsume(DLQ_QUEUE_NAME, false, dlqDeliverCallback, consumerTag -> {});
}
}DLX Routing Keys Deep Dive
When a message is dead-lettered, it's published to the DLX. The routing key used for this publication is important:
- By default, the original routing key of the message is used.
- If you specify
x-dead-letter-routing-keywhen declaring the main queue, this new key will override the original one.
This allows you to route dead-lettered messages to different dead-letter queues based on their original context or specific error types.
Reprocessing Dead-Lettered Messages
The purpose of a DLX is not just to store failed messages, but to provide a pathway for recovery. Common strategies for handling messages in the DLQ include:
- Manual Intervention: Inspecting messages in the DLQ and manually re-publishing them after fixing the underlying issue.
- Logging & Alerting: Simply logging the dead-lettered message details and triggering alerts for investigation.
- Automated Retry Service: A dedicated consumer for the DLQ that attempts to reprocess messages after a delay, possibly with an exponential backoff.
- Separate Error Service: Routing dead-lettered messages to a specific service designed solely for error handling and reporting.
DLX Knowledge Check
Which of the following conditions can lead to a message being dead-lettered by RabbitMQ?
Recap: Dead Letter Exchanges
In this lesson, we explored Dead Letter Exchanges (DLX) in RabbitMQ. You learned:
- DLX provides a robust error-handling mechanism for unprocessable messages.
- Messages are dead-lettered due to consumer rejection, TTL expiration, or queue length limits.
- You configure a main queue with
x-dead-letter-exchangeandx-dead-letter-routing-key. - Practical examples showed how to set up DLX for rejected and expired messages.
- We discussed strategies for reprocessing messages from a dead-letter queue.
DLX is a crucial tool for building resilient and fault-tolerant messaging systems.
الأسئلة الشائعة
هل درس «تبادلات الرسائل الميتة (DLX)» مجاني؟
نعم — نص درس «تبادلات الرسائل الميتة (DLX)» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة RabbitMQ Messaging & Async Systems، انتقل إلى CoddyKit PRO. تتضمن دورة RabbitMQ Messaging & Async Systems 4 دروس في المجموع.
ماذا ستتعلم في «تبادلات الرسائل الميتة (DLX)»؟
هيّئ تبادلات الرسائل الميتة للتعامل مع الرسائل التي يتعذر توصيلها أو معالجتها بنجاح. ونفّذ استراتيجيات قوية لمعالجة الأخطاء وإعادة معالجة الرسائل. تتمرن على RabbitMQ Messaging & Async Systems مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ RabbitMQ Messaging & Async Systems؟
لا تُشترط خبرة سابقة. RabbitMQ Messaging & Async Systems على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «تبادلات الرسائل الميتة (DLX)»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس RabbitMQ Messaging & Async Systems هذا؟
نعم. كل درس في RabbitMQ Messaging & Async Systems يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- التعمّق في تبادل Headers
- ارتباطات التبادل بالتبادل
- تبادلات الرسائل الميتة (DLX)
- التبادلات البديلة للرسائل غير القابلة للتوجيه