강의

멘토링

커뮤니티

Cộng đồng Hỏi & Đáp của Inflearn

Hình ảnh hồ sơ của gnstjrdlsla5350
gnstjrdlsla5350

câu hỏi đã được viết

Nhập môn Kafka áp dụng ngay vào thực tế

[Thực hành] Lưu trữ riêng các tin nhắn thất bại ngay cả khi đã thử lại trong Kafka bằng Spring Boot (DLT, Dead Letter Topic)

잘못된 이메일 주소로 인해 발송 실패 코드

Viết

·

91

0

POSTMAN에
to: fail@naver.com 으로 변경하여 send할 시

총 5번 재시도 후 강사님 처럼 멈추지 않고 아래 에러가 무한 반복이 됩니다.

 

2026-01-04T20:45:42.814+09:00 ERROR 20160 --- [ner#0.dlt-0-C-1] k.r.DeadLetterPublishingRecovererFactory : Record: topic = email.send.dlt, partition = 0, offset = 2, main topic = email.send threw an error at topic email.send.dlt and won't be retried. Sending to DLT with name email.send.dlt.

org.springframework.kafka.listener.ListenerExecutionFailedException: Listener failed

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.decorateException(KafkaMessageListenerContainer.java:3011) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:2911) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeOnMessage(KafkaMessageListenerContainer.java:2877) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeRecordListener(KafkaMessageListenerContainer.java:2787) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeWithRecords(KafkaMessageListenerContainer.java:2629) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeRecordListener(KafkaMessageListenerContainer.java:2523) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeListener(KafkaMessageListenerContainer.java:2161) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeIfHaveRecords(KafkaMessageListenerContainer.java:1538) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.pollAndInvoke(KafkaMessageListenerContainer.java:1476) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.run(KafkaMessageListenerContainer.java:1345) ~[spring-kafka-4.0.1.jar:4.0.1]

at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[na:na]

at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

Caused by: org.springframework.kafka.listener.TimestampedException: Exception thrown at 2026-01-04T11:45:42.814281400Z

at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.onMessage(KafkaBackoffAwareMessageListenerAdapter.java:100) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.onMessage(KafkaBackoffAwareMessageListenerAdapter.java:49) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:2898) ~[spring-kafka-4.0.1.jar:4.0.1]

... 10 common frames omitted

Caused by: org.springframework.kafka.listener.ListenerExecutionFailedException: invokeHandler Failed

at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.checkAckArg(MessagingMessageListenerAdapter.java:519) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:496) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invoke(MessagingMessageListenerAdapter.java:425) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:92) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:52) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.invokeDelegateOnMessage(KafkaBackoffAwareMessageListenerAdapter.java:106) ~[spring-kafka-4.0.1.jar:4.0.1]

at org.springframework.kafka.listener.adapter.KafkaBackoffAwareMessageListenerAdapter.onMessage(KafkaBackoffAwareMessageListenerAdapter.java:97) ~[spring-kafka-4.0.1.jar:4.0.1]

... 12 common frames omitted

Caused by: java.lang.IllegalStateException: No Acknowledgment available as an argument, the listener container must have a MANUAL AckMode to populate the Acknowledgment.

... 19 common frames omitted

kafkaEDAmsadevopsinfrastructure

Câu trả lời 3

0

안녕하세요, 저도 같은 코드로 실습하면서 동일한 현상이 발생하였습니다!

 

consume() 메서드에는 별도의 Acknowledgment를 파라미터를 요구하지는 않습니다.

 

조금 알아보니 spring boot가 4.0.2 버전으로 올라오면서 @RetryableTopic의 dltStrategy 설정 기본 값이 기존에 FAIL_ON_ERROR 에서 ALWAYS_RETRY_ON_ERROR 로 변경 된 것으로 보입니다!

 

값이 ALWAYS_RETRY_ON_ERROR 일 경우, 생성된 dlt 토픽의 메시지를 consume() 메서드를 통해 처리하는 동작인 것 같습니다. 그렇기에 무한 루프를 통해 예외가 무한정으로 쌓이는 것이구요..!저는 아래와 같이 dltStrategy 값을 별도로 설정하니 해결되었습니다!

dltStrategy = DltStrategy.FAIL_ON_ERROR


질문을 새로 올리기보다는 해당 글에 답글로 추가 문의 드리면 좋을 것 같아서 답글 남깁니다!

jscode님의 프로필 이미지
jscode
Người chia sẻ kiến thức

안녕하세요! 강훈님 !

원인 파악과 해결책 공유해 주셔서 정말 감사합니다~~

말씀해 주신 대로 dltStrategy = DltStrategy.FAIL_ON_ERROR로 설정하시면 무한 루프 문제가 해결되는 게 맞아요 !

DLT 처리가 실패했을 때 ALWAYS_RETRY_ON_ERROR 설정이면 해당 레코드를 다시 DLT로 보내서 재시도하게 되는데, DLT 리스너에서 계속 실패하면 무한 반복이 발생하게 되거든요 ~

FAIL_ON_ERROR로 설정하면 DLT 처리 실패 시 재전송 없이 종료되어서 루프가 끊기게 돼요 !

다만 한 가지 보충 드리자면, "Spring Boot 4.0.2에서 기본값이 변경되었다"는 부분은 조금 달라요~

Spring Kafka 공식 문서와 Javadoc을 확인해 보니 @RetryableTopic이 처음 도입된 2.7 버전부터 dltStrategy의 기본값은 ALWAYS_RETRY_ON_ERROR였어요 !

Spring Boot 4.0에서 Spring Kafka의 retry 기능이 기존 Spring Retry에서 Spring Framework 코어로 이전되면서 내부 에러 핸들링 동작 방식이 달라졌어요! 이 과정에서 3.x에서는 DLT 처리 실패 시 조용히 넘어갔던 부분이 4.0에서는 더 엄격하게 재시도하도록 변경된 것으로 보여요 ! 그래서 기본값은 같지만 실제 동작이 달라져서 3.x에서는 문제없던 코드가 4.0에서 무한 루프가 발생하게 된 것 같네요 :)

제가 처음 원인 파악을 할 때는 버전을 미처 고려하지 못했는데, 해결책을 직접 찾아서 공유해 주신 덕분에 같은 문제를 겪으실 다른 수강생분들께도 큰 도움이 될 것 같아요 ! 감사합니다 😊

아 그렇군요! 제가 해결한 이슈에 대해 급하게 공유하느라 정확한 정보를 전달하지 못했네요..! 주신 피드백 덕분에 더 깊이 있는 학습이 된 것 같습니다 감사합니다 😁

0

jscode님의 프로필 이미지
jscode
Người chia sẻ kiến thức

안녕하세요 ! 질문 잘해주셨어요 !

질문해 주신 내용에 대해 답변드려볼게요 ~

 

에러 로그를 보니 DLT 토픽으로 메시지를 보내는 과정에서 문제가 발생하고 있네요!

에러 메시지에 No Acknowledgment available as an argument, the listener container must have a MANUAL AckMode to populate the Acknowledgment.라고 나오는 걸 보면, DLT Consumer 코드에서 Acknowledgment를 파라미터로 사용하려고 했는데 Kafka 리스너 설정이 MANUAL AckMode로 되어있지 않아서 발생하는 문제예요 !

해당 실습과 관련 없는 코드로 인해 발생한 문제 같아요 ~

우선 EmailSendConsumer 클래스의 consume 메서드를 확인해 보시는 걸 추천드려요 ! 이 메서드는 String message 파라미터 하나만 받아야 해요 !

혹시 Acknowledgment 같은 다른 파라미터가 추가되어 있다면 제거해 주셔야 해요 !

다시 한번 수업자료와 코드 비교해 보시고 수업 자료대로 코드를 작성하셨는데도 같은 에러가 발생한다면 사용하신 코드들, application.yml과 함께 에러가 발생했을 때의 전체 로그들을 알려주시면 더 정확하게 도와드릴 수 있을 것 같아요 :)

 

추가로 또 궁금하신 점 있으시면 언제든 편하게 질문 남겨주세요~~

0

안녕하세요, 인프런 AI 인턴입니다. gnstjrdlsla님!

Kafka를 이용한 이메일 전송 시 ListenerExecutionFailedException이 무한 반복되는 문제를 겪고 계신 것 같습니다. 이 문제는 주로 잘못된 이메일 주소로 인해 메시지가 전송되지 않으면서 발생할 수 있습니다. 여러 가지 가능한 원인과 해결 방법을 아래에 정리해 드릴게요.

문제의 핵심 원인

  1. 잘못된 이메일 주소로 인한 재시도 실패:

    • 말씀해주신 대로, 잘못된 이메일 주소가 반복적으로 실패하게 되면 이 문제가 발생할 수 있습니다.
  2. DLT (Dead Letter Topic) 관련 설정 문제:

    • DeadLetterPublishingRecovererFactory 메시지를 확인해보면, DLT로 넘어가야 하는 메시지가 제대로 처리되지 않는 것 같습니다. 설정을 잘 확인해볼 필요가 있습니다.
  3. 수동 AckMode 문제:

    • 로그에서 java.lang.IllegalStateException: No Acknowledgment available as an argument 에러가 보이는데, 이는 수동 ACK 설정이 필요함을 나타냅니다.

해결 방안

  1. 재시도 및 DLT 설정 확인:

    • @RetryableTopic 어노테이션의 설정을 정확하게 검토해 주세요. 특히 재시도 횟수(attempts)와 DLT 관련 설정(dltTopicSuffix)이 의도한 대로 설정되어 있는지 확인해야 합니다.
  2. ACK Mode 설정:

    • 수동 확인 모드를 설정하면, Listener가 메시지를 수동으로 확인해야 처리되도록 설정 가능하며, 관련 설정을 AckMode.MANUAL로 변경하고, Acknowledgment 객체를 주입 받아 수동으로 ack 처리하는 방식으로 구현할 수 있습니다.
  3. 로그 분석 통한 원인 파악:

    • DLT로 메시지를 정상적으로 보내는지, 해당 메시지가 어떻게 처리되고 있는지 더욱 자세히 로그를 남기도록 설정하여 문제의 근본적인 원인을 확인할 수 있습니다.

유사한 질문 및 해결 방안 링크

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

Hình ảnh hồ sơ của gnstjrdlsla5350
gnstjrdlsla5350

câu hỏi đã được viết

Đặt câu hỏi