들어가며
Kafka와 같은 큐 서비스를 사용할 때 메시지가 유실되거나 중복처리될 수 있는 상황이 있습니다.
어떤 구간에서 발생하고 어떻게 대처할 수 있는지 알아보겠습니다.
메시지 전달 보증 수준
일반적인 큐 서비스에서 메시지 전달 보증 수준은 아래와 같습니다.
- At Most once: 1회 전송을 시도. 메시지가 중복되지 않지만 유실될 가능성 있음.
- At Least once: 최소 1회 전송. 메시지가 절대 유실되지 않지만 중복 처리될 수 있음.
- Exactly once: 1회만 전송. 메시지가 절대 유실되지 않으며 컨슈머에서 중복처리 하지 않음.
Kafka의 특징 중 하나가 Exactly-once를 보장할 수 있는 것입니다. (3.0 버전 이후로 Default 설정이 At Least once에서 Exactly once로 변경되었습니다.)
메시지 중복, 유실이 발생하는 구간
메시지 중복과 유실이 발생하는 구간에 대해 살펴보고 원인과 해결 방법을 설명하도록 하겠습니다.
Producer -> Broker
- Broker가 메시지를 받아서 저장했지만, Ack를 보내지 못해 Producer에서 메시지 재전송으로 인해 중복이 발생할 수 있습니다. (브로커에서 ack를 안보내서 중복)
- Producer와 Broker간의 네트워크 문제로 인해 메시지를 보내지 못할 경우 유실이 발생할 수 있습니다. (전송 자체를 실패)
-> Producer에 acks option = all 설정과 retries 횟수를 적절히 지정, enable.idempotence을 설정하여 대처할 수 있습니다.
acks option
acks = 0
- Producer는 Broker에 메시지를 전송한 후 ack를 받고 성공으로 간주합니다.
- 메시지 유실이 발생할 수 있지만 빠르게 메시지를 전송할 수 있습니다.
acks = 1
- Producer는 Broker의 Leader의 ack를 받고 성공 처리합니다.
- 해당 경우 Leader가 메시지를 받고 Follower에 복제 전에 Leader에서 장애 발생시 메시지 유실 가능성이 있습니다.
- At Most Once 수준을 보증합니다.
acks = all (-1)
- Producer는 Broker의 Leader와 Follower의 모든 ack를 받고 성공 처리합니다.
- 속도는 느리지만 가장 강력한 보증 방식이며, At Least Once 수준을 보증합니다.
enable.idempotence
프로듀서의 멱등성 설정으로 중복 메시지 전송시에 카프카에서 한번만 처리되도록 하는 설정입니다.
enable.idempotence를 true로 설정해두면 메시지의 시퀀스 ID를 체크하여 메시지 단일 생성을 보장해줍니다.
Borker -> Consumer
- Consumer 서버가 down 시에 브로커에 메시지가 적재될 경우 유실될 수 있습니다.
- Consumer가 Broker에 commit 하지 않아 메시지를 중복 처리할 수 있습니다.
-> auto.offset.reset = earliest, AcksMode=MANUAL_IMMEDIATE, spring kafka listencer graceful shutdown 설정으로 대처할 수 있습니다.
auto.offset.reset
토픽에 오프셋 정보가 존재하지 않을 때 어떤 기준으로 메시지를 읽을지에 대한 설정입니다.
- latest: 이후 적재되는 메시지부터 (default)
- earliest: 가장 처음 메시지부터
- none: 예외 발생
earliest로 설정하여 메시지 유실을 방지합니다.
AcksMode
컨슈머에서 메시지를 읽고 커밋하는 시점에 대한 설정입니다.
- MANUAL: ack 메서드 호출시 다음 poll() 때 커밋
- MANUAL_IMMEDIATE: ack 메서드 호출 즉시 커밋
MANUAL_IMMEDIATE로 설정하여 중복 처리를 방지합니다.
Consumer auto commit 문제
Consumer auto commit 설정으로 인해 리밸런스나 장애 발생시 메시지가 유실되거나 중복 처리될 수 있습니다.
문제 상황
기본적으로 enable.auto.commit 설정이 true로 되어있어, 메시지를 poll() 한 후 일정 주기마다 자동으로 오프셋을 커밋합니다.
이때 메시지 처리 전에 자동으로 커밋된다면, 메시지 처리가 실패했는데 오프셋이 커밋되어 메시지가 유실될 수 있습니다.
컨슈머가 poll() 메서드로 N개의 메시지를 가져와서 처리 중에 커밋이 이루어지기 전에 장애가 발생하거나 리밸런싱이 발생한다면,
컨슈머가 커밋되기 전의 메시지들을 다시 poll() 해오기 때문에 중복 처리될 수 있습니다.
해결 방법
메시지를 처리한 후에 커밋될 수 있도록 수동 커밋 모드로 설정하여 해결할 수 있습니다.
enable.auto.commit 설정을 false, AckMode를 MANUAL_IMMEDIATE로 설정하여,
수동 ack 메서드 실행 즉시 커밋하도록 하여 중복 처리를 방지합니다.
추가 해결 방법으로 유실의 경우 DLT를 구성하여 처리 실패한 메시지들을 재처리 할 수 있으며,
중복의 경우 멱등성 보장 로직을 구성하여 해결할 수 있습니다.
'Back-end' 카테고리의 다른 글
분산락도 락이다. (Redisson, MSA 환경 동시성 제어 테스트) (0) | 2025.02.10 |
---|---|
Spring Webflux, MVC + 성능 테스트 (0) | 2025.01.31 |
싱글톤 빈 동시성 문제.. 내가 당할 줄은 몰랐지.. (0) | 2025.01.10 |
[DB] 파티셔닝, 샤딩, 레플리케이션 (0) | 2024.12.16 |
✨ MSA 환경에서 분산 트랜잭션 (2PC, SAGA) (1) | 2024.12.02 |