Dead Letter Queue란 무엇인가?
개요
- Dead Letter Queue(Topic)란 무엇인가?
- Dead Letter Queue를 사용하는 이유는 무엇인가?
- Deale Letter Queue 전략
Dead Letter Queue란?
DLQ(Dead Letter Queue) , DLT(Dead Letter Topic)로도 불리며 Message Queue에서 소비되지 못한 메시지가 이동할 수 있는 Queue 또는 Topic을 뜻합니다.
Kafka의 경우에는 Topic이지만 용어의 통일성을 위하여 Dead Letter Queue라고 명칭 하겠습니다.
Consumer Application 내의 잘못된 조건이나 예기치 않은 상태 변경 등 다양한 문제로 메시지가 처리되지 않을 수 있습니다.
Consumer Application이 메시지를 처리하려고 했지만 예외가 발생하였고 지정된 횟수만큼 재시도하였을 때 DLQ로 이동하게 됩니다.
Kafka에서 메시지가 예외가 발생하면 어떻게 될까?
만약 성공 응답이 없으면 kafka가 새로운 메시지를 받지 않고 무한으로 재처리될까요?
2023-06-03T08:57:16.573Z ERROR [order-query-side,,] 1 ---
[org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1]
o.s.kafka.listener.DefaultErrorHandler : Backoff FixedBackOff
{interval=0, currentAttempts=10, maxAttempts=9} exhausted for ORDER-0@0
실제로 호출해 보면 10번 정도 Consume을 시도한 뒤 더 이상 시도하지 않습니다.
에러메시지를 살펴보면 DefaultErrorHandler가 maxAttempts(최대 시도 횟수)와 currentAttepts(현재 시도 횟수)를 보여줍니다.
이때도 실패하는 경우 해당 메시지를 건너뛰어 다시 처리하지 않습니다.
Dead Letter Queue를 사용하는 이유?
- 실패한 메시지는 DLQ에 들어가기 때문에 다른 메시지의 소비가 차단되지 않아 처리량이 증가합니다.
- 각각 별도의 DLQ로 재처리를 수행할 수 있습니다.
- 실패에 대한 재처리를 통하여 정합성을 지킨다!
개발자의 실수, 네트워크 오류, 서비스에 발생한 장애 등이 발생하게 되면 메지시가 소비되지 않을 수 있습니다.
개발자가 문제가 있는 메시지를 조사하여 잠재적인 문제를 조기에 해결할 수 있는 공간을 제공합니다.
더 나아가서는 항상 발생하지 않고 때때로 발생하는 경우라면 DLQ에서 보완 작업을 수행해서 수작업을 막을 수 있습니다.
Dead Letter Queue를 활용하는 다양한 전략예시
Network 문제, 다른 서버의 일시적인 장애, 데이터베이스의 실패
Confluent에서는 이를 Local Retry라고 부르며 N회 재처리를 수행해 보는 전략을 취해볼 수 있습니다.
사실 이런 경우는 DLQ에 메시지를 넣더라도 네트워크 문제나 다른 서버의 장애, 데이터베이스의 실패가 복구되기 전까지는 메시지가 Consume 되지 않을 것입니다.
그러면 실패에서 복구한 걸 인지하는 경우 어떻게 처리할 수 있을까요?
- DLQ로 넣은 뒤 Consumer를 On Off 하여 복구되는 경우 Consumer를 On 합니다.
- DB에 진행상황을 쌓아놓고 Batch를 통해 처리되지 않은 메시지나 로직을 실행시킵니다.
여러 API를 호출해야 하는 경우? C에서만 Fail이 났어요..
Local Retry를 수행해도 해결되지 않는 경우에는 DLQ로 메시지를 보내고 C에 대해서만 Retry를 수행해 볼 수 있습니다.
마찬가지로 Client C에 장애가 발생한 경우라면 해결되기 전까지는 DLQ에서 재시도하더라도 해당 Client가 복구되기 전까지는 해결되지 않습니다.
같은 이유로 계속 실패하는 경우
- 파싱 에러
- 애플리케이션 버그
- 메시지 호환성 이슈
이런 경우에 반복하지 않은 예외 Class들을 정의해 두고 메시지를 스킵하기 전 DQL로 전송합니다.
추후에 정상동작을 하도록 변경하고 다시 Topic에 메시지를 발행하거나, Application이 기동 될 때 DTL를 다시 Consuem 합니다.
메시지 처리가 복잡하거나 atomic 하지 않은 경우
- rollback은 항상 가능하지 않을 수 있습니다.
- 어디서부터 재개해야 할지 모르기 때문에 메시지의 재전송은 어려울 수 있습니다.
as-is
to-be
복잡한 프로세스를 3개의 Consumer로 나누어 단일 로직을 수행하도록 변경하였습니다.
이제 작업들은 멱등하게 만들어 재시도 시 중복이 일어나지 않도록 합니다.
Dead Letter Queue를 사용하면 안 되는 케이스
- 순간 트래픽이 많이 몰려서 backpressure로 활용하는 예시
- 연결 실패에 대한 DLQ
- 순서보장이 필요한 경우
참고자료
https://www.kai-waehner.de/blog/2022/05/30/error-handling-via-dead-letter-queue-in-apache-kafka/
https://medium.com/javarevisited/dead-letter-queue-f5ed22d5594a
https://www.crowdstrike.com/blog/three-best-practices-to-effectively-manage-failed-messages/
https://www.uber.com/en-KR/blog/reliable-reprocessing/