[Spring] kafka 파헤치기
현재 캡스톤 프로젝트에는 사용자 개인의 마인드맵 CRUD 과정이 이루어지는 기능이 들어간다. 간단히 말하자면,1. 사용자가 마인드맵을 생성 (마인드맵의 Root Node 생성)2. 사용자가 마인드맵을 수
sksmsfbrjs51.tistory.com
이전 포스트에서는, Kafka의 필요성과 Kafka가 어떤 구조로 이루어져있는지 분석해보았다.
Kafka를 활용하여 지속적으로 마인드맵 데이터를 Producer에게 전송하여 이를 DB 및 Client에게 전송해줘야 하는 구조임을 알게 되었다.
하지만 여기서 걸리는 것이, 그럼 결국 Publisher에게 호출한 토픽이 같은 경우, 가장 마지막에 요청된 Topic으로 DB에 저장, 수정되거나 사용자에게 반환해줘야 하는게 아닌가? 라는 생각이 들었다.
이에 대해서, Producer의 데이터 멱등성에 대해 파헤쳐보자...
같은 블로그에 정리해주신 분꺼를 참고해보았다
Kafka의 메시지 전달 보장 수준

Kafka는 Producer와 Broker의 설정 및 구성에 따라 메시지 전달 보장 수준을 아래와 같이 세 가지 방식으로 구분할 수 있다.
- 최대 한 번 (At most once): 메시지가 한 번만 전송되며, 재전송은 발생하지 않는다. (메시지 유실 가능)
- 최소 한 번 (At least once): 중복될 수 있지만 메시지가 반드시 전송되며, 최소한 한 번 이상은 전송이 보장된다.
- 정확히 한 번 (Exactly once): 메시지가 정확히 한 번만 전송되며, 재전송은 일어나지 않는다. (메시지 유실 방지 보장)
1. 최대 한 번 (At most once)

'최대 한 번' 전략에서는 메시지가 단 한 번만 전송된다.
이는 메시지에 대한 응답 확인(Ack)을 받지 못하더라도 재전송을 하지 않으며, 이를 통해 중복 전송을 방지한다.
그러나 이 전략은 네트워크 문제 등으로 인해 메시지를 제대로 받지 못하는 경우, 해당 메시지가 유실될 수 있다.
이 전략은 메시지의 손실 가능성을 감수하면서도 중복 전송을 방지하는 접근 방식이며, 높은 처리량과 짧은 대기 시간을 기대할 수 있다.
대량의 로그 수집이나 IoT 등의 환경에서 유용하게 사용됨.
Producer Acks 설정이 acks=0 이다 (producer가 broker의 응답을 기다리지 않음)

2. 최소 한 번 (At least once)

'최소 한 번' 방식에서는 메시지의 중복을 허용하지만, 메시지의 손실은 허용하지 않는다.
Producer는 Broker로부터 응답 확인(ACK)을 받지 못했을 때는 두 가지 경우이다.
- Broker가 메시지를 저장은 했지만 ACK만 전송하지 못한 경우
- Broker가 메시지 저장과 응답 전송 모두 실패한 경우
Broker는 위 두 경우에 대해 판단할 수 없으므로, ACK를 받을 때까지 메시지를 전송한다.
첫 번째의 경우, 프로듀서는 응답을 받지 못했기 때문에 같은 메시지를 재전송하게 되므로, 중복된 메시지가 브로커에 저장될 수 있다.
즉, 이 전략은 메시지의 손실을 방지하기 위해 중복 저장을 허용하는 접근 방식
- 데이터의 완전성을 최우선으로 두는 경우에 매우 유용하다.
Producer Acks 설정은, acks=1이다.

acks=1 설정에서는 Producer가 오직 Leader가 데이터를 저장한 시점에만 응답을 받기 때문에, Leader 이외의 Replica들이 데이터를 성공적으로 복제했는지는 확인하지 못한다.
따라서 Leader Broker가 갑자기 오프라인 상태가 되고, Replica들이 아직 데이터를 복제하지 못한 경우에는 데이터 손실이 발생할 수 있다.
만약 Broker가 확인 요청을 받지 못했다면 Producer는 데이터 쓰기를 위해 요청을 재시도할 수 있다.
즉, 재시도를 통해 중복 전송을 허용하지만, Producer가 Leader Broker를 통해 데이터 쓰기가 성공했는지 확인할 수 있다는 장점이 있음.
3. Exactly once(정확히 한 번)

'정확히 한 번' 방식은 메시지가 딱 한 번만 전달되며, 어떠한 메시지도 손실되지 않아야 한다.
멱등성(idempotence) 원칙을 가장 잘 따르는 방식으로, 프로듀서 ID와 시퀀스 번호를 활용해 응답 확인(Ack)을 받고 메시지를 전송하는 과정이 필수적임.
다른 방식에 비해 응답 대기 시간이 길어지고, 처리량이 상대적으로 떨어진다는 단점이 있다.
이 방식은 메시지가 유실 없이 정확히 한 번만 전송되는 것을 보장해야 하는 상황에 사용됨.
Producer의 Acks 설정은 Acks=all 또는 acks=-1이다.

프로듀서는 리더 브로커를 포함한 모든 레플리카 브로커들로부터 확인 응답을 받은 시점에 메시지 쓰기가 성공했다고 판단한다.
(Kafka 3.x 버전에서는 default 설정임)
acks=1 설정에 비해 더 높은 데이터 안정성을 제공하는데, Producer는 모든 Replica Broker들이 데이터를 성공적으로 저장했다는 확인을 받기 때문에 어떠한 브로커에 문제가 발생하더라도 데이터 손실 위험이 크게 줄어든다.
하지만 모든 레플리카 브로커들로부터 응답을 받아야 하므로, 네트워크 지연이나 브로커의 부하 등으로 인해 전체 메시지 전송 시간이 늘어날 수 있다.
그러나, 여전히 중복 전송이 가능하다는 문제가 존재한다.

위 그림은 Producer가 Broker에게 'x' 라는 메시지를 전송하고, Broker가 Producer에게 ACK를 보내는 과정이다.

하지만, 응답이 네트워크 지연 등의 이유로 프로듀서에게 돌아오지 않는 상황을 가정해보자.
'y'라는 메시지를 Producer가 Broker에게 보냈는데, 이 메시지는 Broker에 의해 정상적으로 파티션에 저장되었지만 ack가 도착하지 못한 상황이다.
이러한 경우 Producer는 오류로 판단하고 동일한 메시지인 'y'를 다시 보내게 되고, Broker에는 'y'메시지가 중복으로 저장되는 것이다.
따라서 멱등성 있는 '정확히 한 번(Exactly once)'을 구현하기 위해서는 ACK 설정뿐만 아니라 idempotent 설정 그리고 Broker의 max.in.flight.requests.per.connection 설정, min.insync.replicas 설정,retries도 같이 되어야 한다.
멱등성 보장 설정
멱등성(idempotence)을 지킨다는 것은 Producer가 Record를 전송했지만, 네트워크 이슈 등으로 인해 ACK를 받지 못한 경우에도 중복 메시지의 전달 및 저장을 방지하는 것이다.
이를 위해 Kafka는 프로듀서 ID(PID)와 시퀀스 번호를 기반으로 메시지를 추적하고, 동일한 커밋에 대해 중복 처리를 방지한다.
- 멱등성을 보장하기 위한 설정
- acks
- Producer가 요청을 보내고 Leader Broker가 레플리카의 수신을 확인해야 하는 개수를 결정.
- 'all' 또는 '-1'로 설정.
- enable.idempotence:
- 프로듀서가 레코드 쓰기 작업을 단 한 번만 허용할 것인지를 결정.
- 'true'로 설정해야 멱등성을 보장함.
- 카프카 v3.0부터는 acks = all(-1)과 enable.idempotence=true가 default로 적용
- max.in.flight.requests.per.connection
- 한 번에 몇 개의 요청을 전송할 것인지를 결정.
- 1 이상 5 이하로 설정해야 함.
- retries
- 메시지를 전송하기 위해 재시도되는 횟수
- 0 이상으로 설정해야 함.
- min.insync.replicas
- 동기화하는 레플리카의 수를 결정
- acks = all일 경우 보통 2로 설정 (Broker 설정이다)
- acks
위의 모든 설정값을 적용하지 않으면 ConfigException이 발생하므로 주의가 필요함.
enable.idempotence 설정

enable.idempotence=true로 설정하면, 위 이미지처럼 각 Producer에는 고유한 Producer ID(PID)가 할당된다.
Producer는 메시지를 Broker에게 보낼 때마다 이 PID를 포함하며, 각 메시지는 순차적으로 증가하는 시퀀스 번호를 받는다.
Producer가 메시지를 보내는 각 Topic Partition마다 별도의 시퀀스가 유지되고, Broker는 파티션별로 성공적으로 처리된 [PID-시퀀스 번호] 조합 중 가장 큰 값을 추적한다.

위 그림을 보면
Broker는 Producer의 요청이 [PID / Seq] 쌍에서 마지막으로 commit된 메시지보다 시퀀스 넘버가 정확히 1만큼 크지 않은 경우, Producer의 요청을 거부하는 것을 볼 수 있다.
이를 통해 Producer는 실패에 따른 요청 재시도를 할 수 있지만, 모든 메시지는 로그에 정확히 한 번만 기록된다.
하지만 새로운 Producer 인스턴스에는 새로운 고유 PID가 할당되므로, 단일 프로듀서 세션 내에서만 멱등성을 보장할 수 있다.
max.in.flight.requests.per.connection (중요)
의미 : 한 번의 연결에서 동시에 처리할 수 있는 최대 요청 수
이 설정은 멱등성(idempotence)을 보장하는 데 중요한 역할을 한다.
Kafka v0.11 버전 이상에서는 멱등성이 활성화된 Producer의 경우, 이 값을 5 이하로 설정하는 것이 권장된다.
- 이유
- 메시지 순서 보장: Kafka Producer는 같은 Topic Partition에 대해 메시지의 순서를 보장해야 한다.
- max.in.flight.requests.per.connection 값이 5 이하일 때, Producer는 한 메시지가 실패하면 그 후의 메시지들은 서버에 전송되지 않는다. 이러한 동작은 메시지의 순서가 변경되지 않도록 보장한다.
- 이 값이 5보다 크다면, Producer는 재시도하는 동안 다른 메시지들을 전송할 수 있어, 메시지 순서가 뒤바뀔 위험 발생
- 중복 메시지 방지
- Producer는 메시지의 중복 전송을 방지한다.
- max.in.flight.requests.per.connection 값이 너무 크면 네트워크 지연 등의 문제로 인해 중복 메시지가 발생할 가능성이 증가합니다. 따라서, 5 이하로 설정!
- 메시지 순서 보장: Kafka Producer는 같은 Topic Partition에 대해 메시지의 순서를 보장해야 한다.
코드
아래의 KafkaConfig에서 멱등성을 활성화 하는 예제이다.
Properties properties = new Properties();
// 멱등성 활성화 설정
properties.setProperty(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true");
참고
'Infra & Cloud' 카테고리의 다른 글
[Apache Kafka] kafka 파헤치기 (2) | 2025.03.25 |
---|---|
[EC2] 인스턴스 HikariCP Connection 고갈 문제 (1) | 2025.01.28 |
[Nginx] 413 Request Entity Too Large 오류 (0) | 2024.11.27 |