Exactly Once Semantics
Apache Kafka는 대용량 데이터 스트리밍을 위한 분산 플랫폼으로, 실시간 데이터 처리와 높은 처리량을 제공한다. 그러나 데이터의 신뢰성과 일관성을 보장하기 위해서는 메시지 전송 방식에 대한 이해가 필수적이다. 메시지의 전달 보장 수준은 시스템의 요구 사항에 따라 달라질 수 있으며, Kafka는 이를 위해 다양한 메시지 전송 옵션을 제공한다.
이번 포스트에서는 Kafka의 메시지 전송 옵션의 종류와 그 동작 원리에 대해 기술적으로 상세히 설명한다.
메시지 전송 옵션 종류
Kafka에서 제공하는 메시지 전송 옵션은 메시지 전달 보장 수준에 따라 다음 세 가지로 분류된다:
At Most Once (최대 1회 전송)
At Least Once (최소 1회 전송)
Exactly Once (정확히 1회 전송)
각 옵션은 메시지 손실 허용 여부와 중복 처리 가능성에 따라 구분되며, 시스템의 신뢰성 요구 사항에 맞게 선택할 수 있다.
At Most Once (최대 1회 전송)
동작 원리
At Most Once 전송 방식은 메시지가 프로듀서에서 브로커로 최대 한 번 전송되고, 컨슈머에게 최대 한 번 전달되는 것을 의미한다. 이 방식에서는 메시지 손실 가능성이 있지만, 중복 메시지는 발생하지 않는다.
프로듀서 측 동작
전송 후 확인 없음: 프로듀서는 메시지를 브로커에 전송한 후, 전송 성공 여부를 확인하지 않고 다음 메시지를 처리한다.
재시도 없음: 메시지 전송 실패 시 재시도를 하지 않는다.
설정 옵션:
acks=0
: 브로커로부터 ACK(확인 응답)를 기다리지 않는다.retries=0
: 전송 실패 시 재시도하지 않는다.
컨슈머 측 동작
자동 커밋 사용: 메시지를 수신하면 즉시 오프셋(offset)을 커밋(commit)한다.
메시지 처리 전 커밋: 메시지 처리 전에 오프셋을 커밋하므로, 처리 중 장애 발생 시 메시지 손실이 발생할 수 있다.
설정 옵션:
enable.auto.commit=true
: 자동 오프셋 커밋을 활성화한다.
장단점 및 적용 사례
장점:
높은 처리량과 낮은 지연 시간.
메시지 중복이 발생하지 않음.
단점:
메시지 손실 가능성 존재.
메시지의 신뢰성이 낮음.
적용 사례:
데이터 손실이 시스템에 큰 영향을 미치지 않는 경우.
실시간 모니터링 데이터, 로그 수집 등.
At Least Once (최소 1회 전송)
동작 원리
At Least Once 전송 방식은 메시지가 프로듀서에서 브로커로 최소 한 번 전송되고, 컨슈머에게 최소 한 번 전달되는 것을 의미한다. 이 방식에서는 메시지 손실은 방지하지만, 메시지 중복이 발생할 수 있다.
프로듀서 측 동작
전송 후 확인: 메시지를 브로커에 전송한 후, 브로커로부터의 ACK를 기다린다.
재시도 전략: 전송 실패 시, 설정된 재시도 횟수만큼 메시지를 재전송한다.
설정 옵션:
acks=all
또는acks=-1
: 모든 ISR(In-Sync Replica) 브로커로부터의 ACK를 기다린다.retries
값 설정: 전송 실패 시 재시도 횟수를 지정한다.enable.idempotence=false
(기본값): 멱등성 프로듀서를 사용하지 않는다.
컨슈머 측 동작
메시지 처리 후 커밋: 메시지를 처리한 후에 오프셋을 커밋한다.
수동 커밋 사용: 자동 커밋을 비활성화하고, 애플리케이션 로직에서 수동으로 오프셋을 커밋한다.
설정 옵션:
enable.auto.commit=false
: 자동 오프셋 커밋을 비활성화한다.auto.offset.reset=earliest
또는latest
: 컨슈머 그룹의 오프셋이 없을 경우의 동작을 지정한다.
장단점 및 적용 사례
장점:
메시지 손실을 방지한다.
메시지의 신뢰성이 높다.
단점:
메시지 중복 가능성 존재.
컨슈머 측에서 중복 처리를 위한 추가 로직 필요.
적용 사례:
데이터 손실이 허용되지 않는 시스템.
중복 처리가 가능한 로그 수집, 이벤트 처리 등.
중복 메시지 처리 전략
아이들포텐트(Idempotent) 처리: 동일한 메시지가 여러 번 처리되더라도 결과가 동일하도록 설계한다.
중복 검출 로직 구현:
메시지 키 또는 메시지의 고유 식별자를 활용하여 중복 여부를 판단한다.
외부 저장소(예: 데이터베이스, 캐시)에 처리된 메시지의 식별자를 저장하고 비교한다.
Exactly Once (정확히 1회 전송)
동작 원리
Exactly Once 전송 방식은 메시지가 프로듀서에서 브로커로 정확히 한 번 전송되고, 컨슈머에게도 정확히 한 번 전달되는 것을 의미한다. Kafka는 **멱등성 프로듀서(Idempotent Producer)**와 트랜잭션(Transactions) 기능을 통해 이 기능을 구현한다.
프로듀서 측 동작
멱등성 프로듀서 활성화:
설정 옵션:
enable.idempotence=true
: 멱등성 프로듀서를 활성화한다.
동작 원리:
프로듀서는 브로커로부터 고유한 **프로듀서 ID(PID)**를 할당받는다.
각 파티션별로 시퀀스 번호를 관리하여 메시지에 부여한다.
브로커는 PID와 시퀀스 번호를 활용하여 중복된 메시지를 식별하고 폐기한다.
트랜잭션 사용:
설정 옵션:
transactional.id
를 고유한 값으로 설정한다.
동작 메서드:
initTransactions()
: 트랜잭션 초기화.beginTransaction()
: 트랜잭션 시작.send()
: 메시지 전송.sendOffsetsToTransaction()
: 컨슈머 오프셋을 트랜잭션에 포함시킴.commitTransaction()
: 트랜잭션 커밋.abortTransaction()
: 트랜잭션 중단.
컨슈머 측 동작
트랜잭션 메시지 소비:
설정 옵션:
isolation.level=read_committed
: 커밋된 트랜잭션의 메시지만 소비한다.
동작 원리:
미완료 또는 중단된 트랜잭션의 메시지는 소비하지 않는다.
오프셋 커밋의 원자성 확보:
메시지 처리와 오프셋 커밋을 프로듀서의 트랜잭션에 포함시켜 원자적으로 처리한다.
sendOffsetsToTransaction()
메서드를 사용하여 컨슈머의 오프셋 정보를 프로듀서의 트랜잭션에 포함시킨다.
브로커 측 동작
트랜잭션 코디네이터(Transaction Coordinator):
프로듀서의
transactional.id
를 기반으로 트랜잭션 코디네이터가 지정된다.트랜잭션 상태(PENDING, COMMITTED, ABORTED)를 관리한다.
트랜잭션 상태 저장:
내부 토픽
__transaction_state
에 트랜잭션 정보를 저장한다.장애 복구 시 트랜잭션 상태를 복원한다.
장단점 및 적용 사례
장점:
메시지 손실과 중복 없이 정확히 한 번만 메시지를 전달하고 처리한다.
데이터 일관성과 신뢰성이 매우 높다.
단점:
설정 및 구현의 복잡성 증가.
추가적인 성능 오버헤드.
일부 외부 시스템과의 연계 시 추가적인 트랜잭션 관리 필요.
적용 사례:
데이터의 정확성과 일관성이 필수적인 금융 거래, 주문 처리 시스템 등.
메시지 중복이나 손실이 비즈니스에 치명적인 영향을 미치는 경우.
Exactly Once Semantics의 내부 메커니즘
프로듀서 ID와 시퀀스 번호 관리
프로듀서는 각 파티션별로 시퀀스 번호를 관리한다.
브로커는 수신한 메시지의 PID와 시퀀스 번호를 검사하여 중복 메시지를 식별한다.
트랜잭션 처리 흐름
트랜잭션 초기화:
initTransactions()
를 호출하여 트랜잭션을 초기화하고 PID를 할당받는다.트랜잭션 시작:
beginTransaction()
을 호출하여 트랜잭션을 시작한다.메시지 전송: 트랜잭션 내에서 여러 메시지를 전송한다.
오프셋 전송:
sendOffsetsToTransaction()
을 사용하여 컨슈머 오프셋을 트랜잭션에 포함시킨다.트랜잭션 커밋:
commitTransaction()
을 호출하여 트랜잭션을 커밋한다.트랜잭션 중단: 오류 발생 시
abortTransaction()
을 호출하여 트랜잭션을 중단한다.
컨슈머의 트랜잭션 메시지 소비
isolation.level=read_committed
설정 시, 컨슈머는 커밋된 메시지만 소비한다.트랜잭션 중단 시 메시지는 자동으로 폐기되며, 컨슈머에게 노출되지 않는다.
Exactly Once 구현 시 주의 사항
프로듀서 설정
enable.idempotence=true
반드시 설정.transactional.id
를 고유하게 설정하여 트랜잭션 상태를 관리한다.acks=all
로 설정하여 모든 복제 브로커로부터의 ACK를 기다린다.max.in.flight.requests.per.connection=5
이하로 설정하여 메시지 순서 보장.
컨슈머 설정
enable.auto.commit=false
로 설정하여 수동 오프셋 커밋 사용.isolation.level=read_committed
로 설정하여 커밋된 메시지만 소비.
성능 고려사항
트랜잭션 사용으로 인한 성능 저하를 감안하여 시스템 설계.
트랜잭션 지속 시간(
transaction.timeout.ms
)을 적절히 설정하여 리소스 낭비 방지.
외부 시스템과의 트랜잭션 일관성
Kafka 외부의 데이터베이스나 시스템과의 트랜잭션 일관성을 유지하기 위해 추가적인 트랜잭션 관리가 필요하다.
분산 트랜잭션(XA 트랜잭션)이나 애플리케이션 레벨에서의 트랜잭션 관리 구현.
Kafka의 메시지 전송 옵션은 메시지 손실 허용 여부와 중복 처리 가능성에 따라 At Most Once, At Least Once, Exactly Once 세 가지로 구분된다. 각 옵션은 시스템의 요구 사항과 데이터 신뢰성 수준에 따라 적절히 선택되어야 한다.
At Most Once: 메시지 손실이 허용되지만 중복 메시지는 발생하지 않음.
At Least Once: 메시지 손실은 방지하지만 중복 메시지가 발생할 수 있음.
Exactly Once: 메시지 손실과 중복 없이 정확히 한 번만 메시지를 전달하고 처리함.
각 전송 옵션의 동작 원리를 깊이 있게 이해하고, 적절한 설정과 구현을 통해 데이터의 신뢰성과 일관성을 보장할 수 있다. 특히 Exactly Once 전송 방식을 구현하려면 멱등성 프로듀서와 트랜잭션 기능을 정확히 이해하고 적용해야 한다.
Last updated