DLT 구현
반복적으로 처리에 실패하는 메시지를 수집하고 저장하도록 DLT를 구성하여 나중에 분석하고 다시 처리할 수 있게 합니다.
DLT 구현은(는) CoddyKit의 무료 Advanced Spring Boot 4: Event-Driven Architecture (Kafka) 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Advanced Spring Boot 4: Event-Driven Architecture (Kafka) 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Advanced Spring Boot 4: Event-Driven Architecture (Kafka) 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Why Dead Letter Topics?
In event-driven systems, consumers sometimes fail to process messages due to transient errors (e.g., database unavailable) or permanent issues (e.g., malformed data).
- What happens to these failed messages?
- Do we retry them indefinitely, blocking the queue?
- Or do we discard them, potentially losing critical data?
This is where Dead Letter Topics (DLTs) come in!
What is a Dead Letter Topic?
A Dead Letter Topic (DLT) is a dedicated Kafka topic where messages that repeatedly fail processing are sent.
- It acts as a 'quarantine' for problematic messages.
- Instead of blocking the main consumer or losing data, messages are moved to the DLT.
- This allows the main consumer to continue processing new messages.
Messages in a DLT can then be inspected, manually corrected, or reprocessed later.
Spring Kafka's DLT Support
Spring for Apache Kafka provides excellent support for DLTs through its error handling mechanisms.
The key component is the DefaultErrorHandler, which can be configured to publish failed messages to a DLT after a certain number of retries.
It uses a DeadLetterPublishingRecoverer internally to perform the actual publishing.
Basic DLT Configuration
To enable DLT for a @KafkaListener, you can configure the DefaultErrorHandler with a DeadLetterPublishingRecoverer. This example sets up a simple DLT after 3 delivery attempts.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.listener.DeadLetterPublishingRecoverer;
import org.springframework.kafka.listener.DefaultErrorHandler;
import org.springframework.util.backoff.FixedBackOff;
@SpringBootApplication
public class DltApplication {
public static void main(String[] args) {
SpringApplication.run(DltApplication.class, args);
}
@Bean
public DefaultErrorHandler errorHandler(KafkaTemplate<?, ?> kafkaTemplate) {
// Publish to DLT after 3 delivery attempts
// FixedBackOff(interval, maxAttempts) -> interval is ignored for DLT after retries
return new DefaultErrorHandler(new DeadLetterPublishingRecoverer(kafkaTemplate),
new FixedBackOff(0L, 2L)); // 0L interval, 2 retries = 3 attempts total
}
@KafkaListener(topics = "my-main-topic", groupId = "my-group", errorHandler = "errorHandler")
public void listen(String message) {
System.out.println("Received: " + message);
if (message.contains("fail")) {
throw new RuntimeException("Simulating processing failure!");
}
}
// To send messages for testing (not part of DLT config itself)
// @Autowired
// private KafkaTemplate<String, String> template;
// @EventListener(ApplicationReadyEvent.class)
// public void sendMessage() {
// template.send("my-main-topic", "Hello");
// template.send("my-main-topic", "This will fail");
// }
}Understanding DLT Topic Names
By default, Spring Kafka names the DLT topic by appending .DLT to the original topic name. For example, if your main topic is my-main-topic, the DLT will be my-main-topic.DLT.
- You can customize this behavior.
- The
DeadLetterPublishingRecovererconstructor can take aBiFunctionto determine the DLT topic and partition. - This allows for more flexible naming conventions or routing failed messages to different DLTs based on criteria.
Message Headers in DLT
When a message is sent to a DLT, Spring Kafka adds several useful headers to it. These headers provide context about why the message ended up in the DLT:
dlt_exception-fqcn: Fully qualified class name of the exception.dlt_exception-message: Message from the exception.dlt_exception-stacktrace: Full stack trace.dlt_original-topic: The topic the message came from.dlt_original-partition: The original partition.dlt_original-offset: The original offset.
These headers are invaluable for debugging and reprocessing.
Customizing DLT Publishing
You can provide a custom DeadLetterPublishingRecoverer to gain fine-grained control over how messages are published to the DLT. This allows you to modify headers, filter messages, or even prevent certain messages from going to the DLT.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.listener.DeadLetterPublishingRecoverer;
import org.springframework.kafka.listener.DefaultErrorHandler;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.backoff.FixedBackOff;
@SpringBootApplication
public class CustomDltApplication {
public static void main(String[] args) {
SpringApplication.run(CustomDltApplication.class, args);
}
@Bean
public DefaultErrorHandler customErrorHandler(KafkaTemplate<Object, Object> kafkaTemplate) {
DeadLetterPublishingRecoverer customRecoverer = new DeadLetterPublishingRecoverer(kafkaTemplate,
(record, exception) -> { // Custom DLT topic/partition resolver
System.out.println("Sending to DLT: " + record.topic() + ".custom.dlt");
return new DeadLetterPublishingRecoverer.HeaderNames(record.topic() + ".custom.dlt", null);
});
return new DefaultErrorHandler(customRecoverer, new FixedBackOff(0L, 1L)); // 1 retry = 2 attempts total
}
@KafkaListener(topics = "another-topic", groupId = "my-custom-group", errorHandler = "customErrorHandler")
public void listenWithCustomDlt(String message) {
System.out.println("Received (custom DLT): " + message);
if (message.contains("fail")) {
throw new RuntimeException("Simulating custom DLT failure!");
}
}
}Consuming DLT Messages
Once messages are in a DLT, you'll need another consumer to process them. This DLT consumer can be designed to:
- Log the error and notify administrators.
- Store the message in a database for manual review.
- Attempt to reprocess the message after a delay or transformation.
It's just another @KafkaListener, but configured to listen to the DLT topic.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.listener.DeadLetterPublishingRecoverer;
import org.springframework.kafka.listener.DefaultErrorHandler;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.util.backoff.FixedBackOff;
@SpringBootApplication
public class DltConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(DltConsumerApplication.class, args);
}
@Bean
public DefaultErrorHandler errorHandler(KafkaTemplate<?, ?> kafkaTemplate) {
return new DefaultErrorHandler(new DeadLetterPublishingRecoverer(kafkaTemplate),
new FixedBackOff(0L, 2L));
}
@KafkaListener(topics = "my-main-topic", groupId = "my-group", errorHandler = "errorHandler")
public void listenMain(String message) {
System.out.println("Main Listener Received: " + message);
if (message.contains("fail")) {
throw new RuntimeException("Main processing failure!");
}
}
@KafkaListener(topics = "my-main-topic.DLT", groupId = "dlt-group")
public void listenDlt(String message,
@Header(KafkaHeaders.RECEIVED_TOPIC) String receivedTopic,
@Header(KafkaHeaders.ORIGINAL_OFFSET) Long originalOffset,
@Header(KafkaHeaders.EXCEPTION_MESSAGE) String exceptionMessage) {
System.out.println("DLT Listener Received: " + message);
System.out.println(" From Topic: " + receivedTopic);
System.out.println(" Original Offset: " + originalOffset);
System.out.println(" Exception: " + exceptionMessage);
// Here you would implement logic to store, alert, or reprocess
}
}DLT Best Practices
To effectively use DLTs, consider these best practices:
- Monitor DLTs: Set up alerts for messages appearing in DLTs, as they indicate persistent issues.
- Process DLTs: Don't let DLTs grow indefinitely. Have a plan to consume and handle these messages.
- Idempotency: Ensure your DLT reprocessing logic is idempotent to avoid duplicate processing issues.
- Separate Concerns: Keep DLT consumers separate from your main application logic for clearer responsibilities.
- Schema Evolution: Be mindful of schema changes when reprocessing old DLT messages.
DLT Quick Check
Which of the following is the primary benefit of using a Dead Letter Topic (DLT) in a Kafka consumer application?
Recap: DLT for Robustness
You've learned how Dead Letter Topics are a crucial component for building robust and resilient Kafka consumer applications.
- DLTs quarantine messages that fail repeated processing.
- Spring Kafka's
DefaultErrorHandlerandDeadLetterPublishingRecoverersimplify DLT integration. - Messages sent to DLTs include useful headers for debugging.
- DLTs require a separate consumer to handle the failed messages.
By implementing DLTs, you ensure your consumers can gracefully handle errors, prevent data loss, and maintain steady message flow.
자주 묻는 질문
“DLT 구현” 강의는 무료인가요?
네 — “DLT 구현” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Advanced Spring Boot 4: Event-Driven Architecture (Kafka) 강의 전체를 잠금 해제할 수 있습니다. Advanced Spring Boot 4: Event-Driven Architecture (Kafka) 강의에는 총 4개의 강의가 포함되어 있습니다.
“DLT 구현”에서 뭘 배우나요?
반복적으로 처리에 실패하는 메시지를 수집하고 저장하도록 DLT를 구성하여 나중에 분석하고 다시 처리할 수 있게 합니다. 브라우저에서 직접 실행하는 실습 코드로 Advanced Spring Boot 4: Event-Driven Architecture (Kafka)을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Advanced Spring Boot 4: Event-Driven Architecture (Kafka)을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Advanced Spring Boot 4: Event-Driven Architecture (Kafka)은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“DLT 구현” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Advanced Spring Boot 4: Event-Driven Architecture (Kafka) 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Advanced Spring Boot 4: Event-Driven Architecture (Kafka) 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.