Message Delivery Semantics

Kafka가 프로듀서에서 컨슈머까지 메시지를 전달할 때 보장하는 신뢰성 수준을 조절할 수 있으며, 이는 시스템의 요구사항에 따라 성능과 데이터 신뢰성 사이의 균형을 맞추는 중요한 설정이다.

At-most-once(최대 한 번 전송)

메시지가 유실될 수는 있지만 절대 중복되지는 않음을 보장하는 의미로, 가장 낮은 신뢰성 수준을 제공하며 최고의 성능을 목표로 할 때 사용된다.

  • Producer: acks=0 설정

    • send() 요청 후 브로커의 응답을 전혀 기다리지 않아, 네트워크 왕복 시간(RTT)을 절약

    • 브로커 장애, 일시적인 네트워크 단절 시 메시지 유실

  • Consumer: 메시지를 처리하기 전 오프셋을 먼저 커밋

    • 자동 커밋 시, 메시지 처리 시간이 auto.commit.interval.ms보다 길 경우, 처리 전에 커밋이 발생할 수 있어 유실 가능

At-least-once(최소 한 번 전송)

메시지가 중복될 수는 있지만 절대 유실되지는 않음을 보장한다는 의미로, 대부분의 애플리케이션에서 기본적으로 채택하는 신뢰성 수준이다.

  • Producer: acks=all / retries > 0 설정

    • 프로듀서는 ISR의 모든 브로커가 메시지를 받았음을 확인할 때까지 대기

    • 브로커가 메시지를 성공적으로 저장했지만, 프로듀서에게 Ack를 보내기 전에 네트워크 문제나 장애가 발생하면, 프로듀서는 타임아웃으로 실패를 감지하고 메시지 재전송

    • 이 시점에 브로커에는 동일한 메시지가 두 번 기록될 수 있음

  • Consumer: 메시지 처리를 완료한 후에 오프셋을 수동으로 커밋

    • enable.auto.commit=false 설정

    • poll()로 받은 메시지 배치의 처리가 모두 끝난 후 commitSync() 또는 commitAsync() 호출

    • 메시지 처리는 성공했지만, commitSync() 호출 전에 컨슈머 애플리케이션에 장애가 발생하면, 다시 메시지를 읽어와 이전에 처리했던 메시지 중복 발생

    • 중복 처리를 방지하기 위해 멱등성을 고려한 설계 필요

이 전송 방식은 컨슈머의 멱등성을 보장하는 것이 중요한데, 이를 구현하기 위한 방법의 예시는 다음과 같다.

  • 데이터베이스 활용: 메시지의 고유 키(예: 이벤트 ID, 주문 ID)를 데이터베이스의 Primary Key나 Unique Key로 사용하여 중복 INSERT를 방지

  • 버전 관리: 데이터에 버전 번호를 두고, 현재 저장된 버전보다 낮은 버전의 데이터가 들어오면 무시

  • 상태 저장소 활용: Redis나 DB에 처리된 메시지 ID를 저장하고, 메시지를 처리하기 전에 ID의 존재 여부를 먼저 확인하여 중복 실행 방지

Exactly-once(정확히 한 번 전송)

메시지가 유실되거나 중복되지 않고, 정확히 단 한 번만 처리됨을 보장하는 가장 강력한 수준의 신뢰성 수준이다.

  • Producer: enable.idempotence=true / transactional.id=<UNIQUE_ID> 설정

    • enable.idempotence=true로 설정하여 브로커로의 메시지 중복 전송 방지

    • transactional.id를 고유하고 안정적인 값으로 설정하여 트랜잭션을 활성화

      • 이를 통해 여러 토픽 파티션에 걸친 '읽기-처리-쓰기' 작업을 하나의 원자적 단위로 묶어줌

  • Consumer: isolation.level=read_committed 설정

    • 컨슈머는 최종적으로 커밋(Commit)된 트랜잭션에 포함된 메시지만 읽도록 설정

    • 만약 트랜잭션이 처리 도중 중단(Abort)되면, 해당 트랜잭션에 포함된 모든 메시지는 컨슈머에게 노출되지 않아 데이터 정합성 보장

위 설정과 트랜잭션이라는 두 가지 기능을 결합하여, Kafka는 프로듀서의 최초 전송부터 컨슈머의 최종 처리까지 모든 과정에서 메시지가 정확히 한 번만 처리되도록 보장할 수 있다.

Last updated

Was this helpful?