배달 못한 편지 교환기(DLX)
전달 또는 처리가 성공적으로 이루어지지 않은 메시지를 처리하도록 배달 못한 편지 교환기를 구성합니다. 견고한 오류 처리와 메시지 재처리 전략을 구현합니다.
배달 못한 편지 교환기(DLX)은(는) CoddyKit의 무료 RabbitMQ Messaging & Async Systems 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 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.
AI 튜터와 함께 RabbitMQ Messaging & Async Systems을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 11
- 레슨
- 44
자주 묻는 질문
“배달 못한 편지 교환기(DLX)” 강의는 무료인가요?
네 — “배달 못한 편지 교환기(DLX)” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 RabbitMQ Messaging & Async Systems 강의 전체를 잠금 해제할 수 있습니다. RabbitMQ Messaging & Async Systems 강의에는 총 4개의 강의가 포함되어 있습니다.
“배달 못한 편지 교환기(DLX)”에서 뭘 배우나요?
전달 또는 처리가 성공적으로 이루어지지 않은 메시지를 처리하도록 배달 못한 편지 교환기를 구성합니다. 견고한 오류 처리와 메시지 재처리 전략을 구현합니다. 브라우저에서 직접 실행하는 실습 코드로 RabbitMQ Messaging & Async Systems을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
RabbitMQ Messaging & Async Systems을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 RabbitMQ Messaging & Async Systems은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“배달 못한 편지 교환기(DLX)” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 RabbitMQ Messaging & Async Systems 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 RabbitMQ Messaging & Async Systems 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- Headers 교환기 자세히 알아보기
- 교환기 간 바인딩
- 배달 못한 편지 교환기(DLX)
- 라우팅할 수 없는 메시지를 위한 대체 익스체인지