본문 바로가기

소프트웨어-이야기/프로그래밍 언어와 프레임워크

Kafka 컨슈밍와 재시도

카프카 이벤트를 컨슈밍할 때, 오류가 발생하는 경우 이를 재시도하는 방안에 대해서 정리해보고자 한다. 

컨슈밍에 실패한 경우, 재시도 방법을 크게 Blocking과 Non-Blocking 으로 나눠볼 수 있다. 

여기서 Blocking은 현재 실행중이 프로세스에서 처리를 재시도하는 것을 말한다. Non-Blocking은 큐 시스템을 활용하여, 비동기로 다시 재시도 처리하는 것을 말한다. 

 

(1) Blocking

대안

a. spring-retry를 사용하여, 원하는 횟수만큼 처리를 재시도하기 

@Service
public interface PaymentService {

    @Retryable(value = TimeoutException.class, maxAttempts = 2, backoff = @Backoff(delay = 100))
    void complete(long orderId) throws SQLException;
}

 

장점

단순하고, 예측하기 쉬운 코드이다. 

단점

  • 지속적인 오류를 발생하는 특정한 케이스가 프로세스를 계속 점유할 수 있다.
  • 일정한 텀을 두고 재시도를 하고 싶은 경우, 한 케이스가 프로세스를 더 오랜시간 점유할 수 있다. 

 

(2) Non-Blocking

대안

a. kafka retry topic 사용하기 

@Component
@KafkaListener(id = "order-group", topics = "payment-complete")
public class PaymentKafkaListener {

    @KafkaHandler
    @RetryableTopic(
      backoff = @Backoff(value = 3000L), 
      attempts = "5", 
      autoCreateTopics = "false",
      include = TimeoutException.class, exclude = NullPointerException.class)
    public void handlePayment(PaymentCompleteEvent event) {
        System.out.println("Event received: " + event);
    }
}

장점

  • 카프카로 Non-blocking 처리가 가능하다.
  • 비동기로 재시도 처리를 구현하기 수월하다.

단점

1. RetryTopic을 사용하면, 재시도 횟수만큼 Retry Topic이 생겨난다. 이러한 Topic들을 관리해야한다.

ex. payment-complete-retry-0, payment-complete-retry-1, payment-complete-retry-2 

2. Retry Topic에서 다시 에러가 발생하는 경우, 예외처리가 까다롭다.

뫼비우스의 토픽

Retry Topic에서 다시 Retry Topic을 만들어서, 무한 Retry에 빠지는 헬을 경험할 수 있다.

 

b. kafka dlt 토픽을 만들고, 재처리하는 consumer를 구현하기

장점

  • 카프카로 Non-blocking 처리가 가능하다.
  • dlt만 처리하면 되기 때문에 처리 구조가 단순하다. 

단점

  • dlt 처리용 consumer를 관리해야한다. 
  • 재시도를 1회만하기 때문에, 재시도 횟수를 제어하기 위한 다른 방안이 필요하다. 

 

c. 컨슈밍에 실패하는 경우, 새로운 카프카 이벤트를 발행하기 

장점

  • 카프카로 Non-blocking 처리가 가능하다.
  • 단순하다.

단점

  • 최대 재시도 횟수를 DB에 저장해두어야한다. 

 


기타: Transactional Outbox 패턴으로 메시지 발행 보장하기

리디북스 기술 블로그: Transactional Outbox 패턴으로 메시지 발행 보장하기