Apache Kafka ‐ 메시지 처리 실패 시 대처 방법 - thought-corner/Backend-PlayGround GitHub Wiki

📚 Kafka에서 처리하지 못한 메시지를 재시도하도록 만들기

Backoff FixedBackOff{interval=0, currentAttempts=10, maxAttempts=9} ...
  • 메시지 의미 해석
    • interval : 재시도를 하는 시작 간격(ms) -> interval=0는 실패하자마자 즉시 재시도한다는 뜻이다.
    • maxAttempts : 최대 재시도 횟수
    • currentAttempts : 지금까지 시도한 횟수

[재시도 정책 변경하기]

@KafkaListener(
    topics = "email.send",
    groupId = "email-send-group" // 컨슈머 그룹 이름
)
@RetryableTopic(
    // 총 시도 횟수 (최초 시도 1회 + 재시도 4회)
    attempts = "5", 
    // 재시도 간격 (1000ms -> 2000ms -> 4000ms -> 8000ms 순으로 재시도 시간이 증가한다.)
    backoff = @Backoff(delay = 1000, multiplier = 2) 
)
  • 보통 재시도 횟수는 3~5회로 정한다. 그 이유는 재시도를 너무 많이 하면 시스템 부하가 커질 우려가 있고 너무 적으면 일시적인 장애애 대응하기 어렵기 때문이다.
  • 첫 재시도 간격은 짧게 설정하는 편이고 그 이후 재시도 간격은 지수적으로 증가하도록 설정하는 편이다. 그래야 일시적인 장애에 대해서 첫 빠른 재시도로 대응이 가능하고 만약 장애가 조금 길어지는 경우라도 무의미하게 재시도를 하는 것을 방지하기 위함이다.

📚 Kafka에서 재시도에 실패한 메시지를 따로 보관하기(DLT, Dead Letter Topic)

  • DLT(Dead Letter Topic)는 오류로 인해 처리할 수 없는 메시지를 임시로 저장하는 토픽이다.
  • Kafka에서는 재시도까지 실패한 메시지를 다른 토픽에 따로 저장해서 유실을 방지하고 후속 조치를 가능하게 한다.

DLT를 사용하는 이유(RabbitMQ의 DLQ, DLX와 거진 비슷한 개념)

  • 실패한 메시지를 DLT 토픽에 저장해놓기 때문에 실패한 메시지의 유실을 방지
  • DLT 토픽에 실패한 메시지가 저장되기 때문에 사후 실패 원인 분석이 가능
  • DLT 토픽에 실패한 메시지가 저장되기 때문에 처리되지 못한 메시지를 수동으로도 처리가 가능

[DLT 활용해 재시도에 실패한 메시지를 따로 보관하기]

@KafkaListener(
    topics = "email.send",
    groupId = "email-send-group" // 컨슈머 그룹 이름
)
@RetryableTopic(
    // 총 시도 횟수 (최초 시도 1회 + 재시도 4회)
    attempts = "5",

    // 재시도 간격 (1000ms -> 2000ms -> 4000ms -> 8000ms 순으로 재시도 시간이 증가한다.)
    backoff = @Backoff(delay = 1000, multiplier = 2),

    // DLT 토픽 이름에 붙일 접미사
    dltTopicSuffix = ".dlt"
)

📚 재시도조차 실패한 메시지 사후 처리 방법

  • DLT에 저장된 메시지는 여러 번의 재시도를 거쳤음에도 불구하고 실패한 메시지이기 때문에 분명 문제가 있는 메시지이다. 따라서 이 메시지는 수동으로 체크하고 조치를 취해야 한다.
    • DLT에 저장된 실패 메시지를 로그 시스템에 전송해 장애 원인을 추적할 수 있도록 한다.
    • DLT에 메시지가 저장되자마자 수동으로 대처할 수 있게 알림을 설정한다.
    • 알림을 받은 관리자는 로그에 쌓인 내용을 보고 장애 원인을 분석하고, 그에 맞게 메시지를 수동으로 처리한다.

메시지를 수동으로 처리하는 방법들

  • 메시지를 원래 토픽으로 직접 다시 보내기
  • 메시지 폐기하기
  • 잘못된 메시지 내용이 Kafka에 들어가지 않게 Producer 검증 로직 보완하기
@Service
public class EmailSendDltConsumer {

    @KafkaListener(
        topics = "email.send.dlt",
        groupId = "email-send-dlt-group"
    )
    public void consume(String message) {

        // ...로그 시스템에 전송하는 로직은 생략...
        System.out.println("로그 시스템에 전송 : " + message);
    
        // ...Slack에 알림 발송하는 로직은 생략...
        System.out.println("Slack에 알림 발송");
    }
}