12_kafka_비동기메시지큐를_활용한_설계 - loveAlakazam/hh-08-concert GitHub Wiki
공식문서에서는 Kafka를 이벤트 스트리밍 플랫폼 으로 정의합니다.
Apache Kafka is an event streaming platform.
Event Streaming Platform
이벤트(event)는 비즈니스 서비스에서 발생하는 사실을 의미합니다. 이벤트들을 실시간으로 수집/저장/전달/처리/확장/복원 기능을 모두 갖춘 시스템을 의미합니다. 예를들면 콘서트 예약, 예약된 좌석 결제 와 같은 비즈니스에서 발생한 상황들을 이벤트로 인식하고, 각 이벤트별로 스트리밍 데이터로 받아들여서 분석하는 것을 의미합니다. 따라서 이벤트 스트리밍 플랫폼은 다양한 시스템에서 발생하는 이벤트를 빠르고 효율적으로 수집하고 처리할 수 있는 환경을 제공합니다.
Kafka는 단순한 메시지 브로커를 넘어서 신뢰성있게 데이터를 실시간으로 저장/전파/처리 할 수 있는 분산이벤트 스트리밍 플랫폼입니다.
Kafka의 특징
- 대용량 처리
- 확장성
- 내구성
- 비동기 통신
- 실시간처리
- 유연한 소비모델
- Kafka에서 메시지는 이벤트(event) 를 의미하며, 이벤트는 비즈니스에서 발생한 '사실/사건'을 의미합니다.
- Kafka에서 메시지는 Key, Value, Timestamp 와 같은 메타데이터로 구성되어있습니다.
- MSA 관점에서의 브로커는 실행된 카프카 애플리케이션 서버 1대 를 의미합니다.
- 보통 하나의 클러스터당 3대 이상의 브로커로 구성하는 편입니다.
- 메시지를 발행(write)하여 Broker에게 전송하는 역할을 하는 서비스 애플리케이션/시스템 을 의미합니다.
- Topic의 특정 파티션으로 메시지를 전송하여 실시간 데이터 스트리밍이나 로깅시스템에 활용됩니다.
- Producer는 효율적인 데이터 전송과 높은 처리량을 달성하기 위해 다양한 설정을 제공합니다.
- Topic에 대한 파티션이 여러개인 경우 Round Robbin 방식으로 데이터를 넣습니다.
Consumer
-
메시지를 구독하는 역할을 담당하는 클라이언트 입니다.
-
Broker로부터 특정 Topic의 파티션에서 메시지를 읽어와서 애플리케이션에 필요한 처리를 수행합니다.
-
Consumer의 역할은 Topic의 Partition으로부터 데이터를 Polling 합니다.
-
Partition의 Offset 위치를 commit하여 기록합니다.
-
Consumer가 Message를 읽어와도 Partition안에 있는 데이터들은 삭제되지 않고 Partition에 그대로 남습니다.
-
Consumer Group 내 Consumer 한개에 장애가 생겨도 다른 Consumer들은 할당된 Partition으로 부터 데이터를 읽어올 수 있습니다.
-
각 Consumer 가 다른 파티션의 데이터를 독립적으로 처리할 수 있도록 합니다. 이는 전체 시스템의 병렬처리 능력과 효율성을 향상 시켜줍니다.
-
Consumer가 실행이 중지되었을 가정할 때, 재실행하면 중지되었던 시점을 기억하여 시작위치부터 다시 복구하여 데이터처리가 가능해집니다.
- Consumer는 Topic의 Partition의 개수만큼 할당이 가능합니다.
- 하나의 파티션은 특정 Consumer Group 안에서 반드시 1개의 Consumer에게 할당되어야합니다.
- Consumer Group 내 Consumer 가 Partition의 숫자보다 많으면 비활성화되는 Consumer 가 발생할 수 있습니다.
- (
Partition 개수 >= Consumer Group 개수
)
- (
Consumer Group
- Consumer 들을 묶는 논리적인 그룹단위 입니다.
- 모든 Consumer들은 하나의 Consumer Group에 속해야합니다.
- Kafka에서는 특정 Partition에 대한 Offset 관리를 Consumer Group단위로 수행합니다.
- Consumer Group에 있는 Consumer들은 다른 Consumer Group에 영향을 미치지 않습니다. 1개의 Topic으로 들어온 데이터는 다양한 역할을 하는 Consumer들이 각자의 방식대로 처리됩니다.
- Consumer Group별로 Topic별로 Offset을 나누어 저장하기 때문입니다.
- 아래 그림으로 예를들면, Consumer Group1과 Consumer Group2 는 서로 영향을 미치지 않습니다.
Kafka에서 리밸런싱은 다음과 같은 상황에서 Consumer와 Partition의 할당관계를 재조정하는 과정입니다.
- Consumer Group에 멤버 추가 / 제거할 때
- Broker 또는 Partition에서 장애가 발생할 때
- Topic의 파티션개수가 변경될 때
Partition이 다른 Consumer로 다시 할당되고, Consumer는 기존 Offset을 기준으로 메시지 소비를 이어갑니다. 다만, 리밸런싱 중에는 짧은 downtime으로 잠시동안 메시지를 소비하지 못할 수 있습니다.
Kafka는 Sticky Assignor
, Incremental Rebalance
, Session Timeout 설정
으로 리밸런싱의 부작용을 줄이는 기능을 제공하고 있습니다.
Topic
- Kafka에서 특정한 데이터스트림이 들어갈 수 있는 공간 을 의미합니다. 파일시스템의 폴더와 같은 역할을 합니다.
- 하나의 Topic에는 여러개의 Partition으로 구성하므로, 최소 1개의 Partition이 있어야합니다.
- Topic은 이름에 의해 구분됩니다.
Particition
- 각 Partition은 서로 독립적입니다.
- Topic을 구성하는 Partition은 정렬되어있습니다. Partition 안에 들어있는 메시지들은 Offset이 오름차순으로 정렬되어있습니다.
- Partition에 한번 쓰여진 데이터는 변경이 불가능합니다.
- Message 데이터에 Key값이 제공되지않으면 랜덤하게 Partition에 할당됩니다.
Offset
- 각 Partition을 구성하는 메시지 데이터에 부여된 고유번호를 의미하며, 0부터 시작합니다.
- Offset 은 토픽별로, 파티션별로 별개로 지정됩니다.
- Consumer가 어느지점까지 읽었는지 확인하기 위한 용도로 사용됩니다.
- Offset값은 Parition 마다 다를 수 있습니다.
- 새로운 Consumer Group을 추가하면, 할당된 Partition 의 초기 Offset이 0으로 초기화합니다.
Consumer가 데이터를 읽어올 때, Offset 처리 순서 설정
-
auto.offset.reset: earliest
: 가장 오래된 메시지 부터 먼저 읽기시작합니다.
아래 그림 예로는 Offset 값이 낮은 메시지부터 순차적으로 읽습니다. 밑의 그림의 예로들면 Offset이 0 -> 1 -> 2 순으로 메시지를 읽습니다.
-
auto.offset.reset: latest
: Patition 안에 이미 들어있는 메시지들은 읽지않고, 이후에 들어온 새로운 메시지를 먼저 읽습니다.
아래 그림으로 예를 들면 Partition에 들어있는 Offset이 0,1,2 인 메시지들은 읽지않고, Producer에서 새로 발행한 신규 메시지 3 -> 4 순으로 메시지를 읽습니다.
Offset Commit 처리 방식
Offset 자동커밋 처리는 Consumer가 메시지를 어디까지 읽었는지(소비 완료지점)을 Kafka에게 알려주는 방식입니다. 이 커밋의 여부에 따라서 메시지 중복처리 방지나, 재처리 여부를 결정합니다.
-
enable.auto.commit: true
(default) : 비명시적 offset commit (자동커밋)
- Kafka Consumer 가 주기적으로(
offset.interval.ms
마다) 자동으로 현재 Offset을 커밋합니다. - 메시지 처리량이 많고, 정확한 일관성이 덜 중요한 경우에 사용합니다.
- 메시지를 처리하기 전에 Offset이 커밋될 수 있어서 장애가 발생할때 데이터 유실 가능성이 있습니다.
- 이미 커밋된 메시지를 다시 재처리가 되므로, 중복처리 가능성이 있습니다.
-
enable.auto.commit: false
: 명시적 offset commit (수동커밋)
- Offset Commit을 개발자가 수동으로 제어하며,
commitSync()
,commitAsync()
호출이 필요합니다. - 정확하게 처리완료후에 커밋을 하므로, 중복처리나 메시지 유실이 발생하지 않아서 높은 정확성을 보장합니다.
- 장애/실패 복구상황에서 재처리 가능성을 컨트롤할 수 있습니다.
- 커밋시점을 개발자가 관리하고 정해야하므로 코드 복잡도가 증가될 수 있습니다.
복잡한 데이터 파이프라인은 배포와 장애대응에 어렵고, 데이터 프로토콜이 여러종류일경우 프로토콜을 변경하려고 할 때 유지보수가 어려워지는 불편함이 있습니다.
Kafka는 Producer 와 Consumer 간의 복잡한 데이터흐름들을 간결하게 나타냄으로써 문제를 해결했습니다.
Pub/Sub 모델의 메시지큐 형태를 갖추기때문에 Producer 와 Consumer 간의 결합도를 낮춰줍니다.
다양한 서비스간에서 발생하는 이벤트들을 빠르고 효율적으로 처리해줍니다.
Kafka 사용 사례
- 여러 서버의 로그 수집
- 이벤트 기반 처리
- IoT 센서 데이터 수집 및 실시간처리
- 데이터 파이프라인
- 사용자 행동 이벤트 기반 푸시/이메일 실시간 알림
👉 Append-Only-Log 구조의 디스크기반 순차쓰기 + Zero-Copy
- Append-Only Log 구조
- 데이터가 항상 끝에 추가(append)되기만 하는 로그파일 구조 입니다. 삭제나 중간 수정없이 오직 순서대로 쓰기 만 하는 구조를 의미합니다.
- Kafka는 Partition 단위로 로그파일을 유지하며, Producer가 전송한 메시지를 로그파일의 맨끝에 순차적으로 저장합니다.
- 메시지를 디스크에 순차적으로 기록하여 랜덤I/O가 없습니다. Random I/O(direct access)는 하드디스크의 드라이브의 플래터(원판)을 돌려서 읽어야할 데이터가 저장된 위치로 디스크 헤더를 이동시킨다음 다음데이터를 읽는 것을 의미합니다.
- Java Util의 ArrayList, ByteBuffer, FileChannel 등은 연속적인 메모리 배열 기반구조로 가지므로 데이터를 계속 추가하는 구조로 되어있습니다. 반면에, LinkedList의 경우에는 메모리가 불연속적이고 파편화되어있습니다. 따라서 ArrayList 와 Append-Only Log 의 동작방식이 매우 유사합니다.
- Zero Copy
- 애플리케이션이 데이터를 처리하지 않고, 커널이 직접 디스크에서 네트워크 소켓으로 복사 하는 기술을 의미합니다.
- Kafka에서는
sendFile()
시스템콜을 호출하여 디스크(데이터 브로커)에 저장된 메시지를 중간에 사용자 공간에 가져오지않고 곧바로 소켓으로 바로 전송합니다. 이로인해 복사단계가 최소화되어 CPU 사용을 최소화 시켜줍니다.
- Page Cache
- OS 파일시스템의 캐시를 적극활용하여 디스크 I/O를 최소화하였습니다.
- Batch 처리 및 압축
- 여러 메시지를 하나의 배치로 전송하여 네트워크 효율성을 극대화했습니다.
👉 수평확장구조 + 병렬처리모델
-
Partition 기반의 분산처리 : Topic이 여러개의 Partition으로 나눠져있기때문에 병렬처리가 가능합니다.
-
Producer/Consumer 병렬처리 가능 : Producer는 각 Partition에 병렬로 메시지를 전송하고, Consumer도 병렬로 메시지를 소비하기 때문입니다.
-
Clustering : 여러개의 Broker가 병렬로 Topic을 분산처리하여 수평확장이 가능하기 때문입니다.
👉 Partition 복제 + 자동 장애 복구 메커니즘
Partition Replication (Topic Replication)
-
Broker 개수에 따라서 Replication 개수가 제한됩니다.
-
원본파티션은 Leader Partition 이고, 복제본 파티션은 Follower Partition 으로 정의합니다.
- 그림의 예시로 들면 Broker가 3대면 replication이 3개 이므로, 파티션 partition#1 의 Leader Partition은 Broker1이 갖습니다. Broker2, Broker3은 각각 복제된 partition#1 Partition을 갖습니다.
-
Producer가 Topic에 Partition에 메시지를 전달할 때, 주로 Leader Partition 에서 메시지 전달을 받습니다.
-
Replication 개수가 많아지면 Broker의 리소스 사용량이 늘어날 수 있기때문에, Kafka에 들어오는 데이터와 저장시간 설정을해서 replication 개수를 정하는 편입니다. 대개 Broker가 3대 이상일때 Broker 개수와 동일한 replication = 3 을 권장하는 편입니다.
-
파티션 partition#1 의 Leader Partition을 보유한 Broker1 이 만일 장애가 발생한다고 가정했을때, 복제본인 Follower Partition을 가진 Broker에서 데이터 복구를 하게되며, 해당 Follower Partition은 Leader Partition으로 승계하게됩니다.
ISR (In-Sync-Replicas)
- ISR(In-Sync-Replica) 는 Leader Partition과 Follower Partition 들의 합 을 의미합니다.
- 설정된
acks
옵션에 따라, Producer 에서 생성된 메시지가 Kafka에서 안전하게 저장 됐는지를 나타냅니다.
acks 옵션
acks 설정 | 의미 | 손실 가능성 |
---|---|---|
acks=0 |
Kafka가 저장 확인여부를 기다리지 않음 | 메시지 손실 가능성 높음 |
acks=1 |
Leader Partition 에만 저장되면 성공처리 | Follower Partition에 복제를 하지않고 응답하기때문에 Leader Partition을 가진 Broker에서 장애가 발생하면 손실될 수 있음. |
acks=all |
Leader Partition + Follow Partition 모두에 저장되면 성공처리 | 메시지 손실 없음 |
Message Queue는 서로다른 시스템간에 비동기적으로 메시지를 전달하기위한 중간 저장소(버퍼)를 의미합니다.
Message Queue 의 특징
- 일대일 메시지 전달 (Point-To-Point)
- 비동기 처리
- 비결합성 (Decoupling)
- 순서보장
- 확장성
- 재시도 및 오류처리
Pub/Sub(Publish-Subscribe) 패턴은 발행자(Publisher)가 메시지를 Topic에 발행하고, 구독자(Subscriber)가 관심있는 주제를 구독하여 메시지를 받는 비동기 이벤트 메시징 모델입니다. 메시지큐와 달리 1개의 메시지를 여러 구독자에게 전달할 수 있습니다.
Pub/Sub 패턴 특징
- 발행자와 구독자의 분리 (Decoupling)
- 확장성
- 비동기 처리
- 특정 Topic 기준으로 전달
- 브로드캐스팅
항목 | RabbitMQ | Kafka |
---|---|---|
메시징 모델 | AMQP(Advanced Message Queuing Protocol)를 구현한 오픈소스 메시지 브로커 | 이벤트 스트리밍 플랫폼 |
전송 방식 | 메시지를 Consumer에게 PUSH 하는 PUSH 모델 | Consumer가 직접 Pull 하는 PULL 모델 |
메시지 저장 | 소비후 삭제(POP) | 디스크에 지속 저장 |
처리 순서 | 큐 단위 FIFO | 파티션 단위 FIFO |
성능/처리량 | 낮은 지연과 빠른 응답 | 높은 확장성과 대용량처리 |
(Case 1) Kafka에서 Consumer가 메시지 처리를 하지 못할 경우 해결방안 - DLT (Dead Letter Topic)
DLT(Dead Letter Topic) 은 Kafka의 Consumer에서 메시지 처리중에 실패가 발생했을 때, 실패한 메시지를 Topic이 아닌 사후처리용 Topic에 보내는 전략입니다. 실패가 발생하게되면 메시지를 버리지 않고, 나중에 따로 분석하거나 재처리할 수 있도록 별도의 공간에 보관하는 방식입니다.
DLT를 사용하는 이유
- 문제 메시지 격리 & 애플리케이션 중단 방지
- 재처리 가능
- 문제 원인 제공하여 디버깅/분석
- 장애격리
DLT 에 담을 수 있는 정보
- 에러메시지
- 예외 Stack Trace
- Original Topic 이름
- Consumer Group ID
- Offset 정보
'메시지 전달 보장방식' 은 어떤 메시지가 몇번 전달됐는지를 보장하는 방식입니다. 애플리케이션이 데이터를 처리하거나 주고받을 때 '중복처리'나 '데이터 유실'을 어떻게 다루는지에 대해 결정해줍니다. Kafka에서는 '메시지 전달 보장방식'이 총 3가지가 있습니다.
방식 | 설명 |
---|---|
At Most Once (최대 한번) |
딱 한번만 메시지를 전달합니다. 중복은 발생하지 않지만, 메시지가 유실될 수 있습니다. |
At Least Once (최소 한번) |
최소 한번 이상 메시지를 전달합니다. 메시지 유실은 없지만, 메시지의 중복전달(두번 이상 전달) 이 발생할 수 있습니다. |
Exactly Once (정확히 한번) |
메시지가 정확히 딱 한번만 전달되도록 보장합니다. 메시지의 중복전달도 없고, 유실도 없습니다. 다만, 성능이 느립니다. |
- MSA에서 데이터베이스의 일관성과 메시지의 발행의 신뢰성을 보장하기 위한 디자인패턴 입니다.
- 서비스의 데이터 변경을 반영하는 이벤트를 Message Broker에 안전하게 발행하면서 트랜잭션의 일관성을 유지하고 장애상황에서도 데이터 손실을 방지하는데 중요한 역할을 담당합니다.
- 데이터베이스 트랜잭션과 이벤트발행을 안전하게 묶어서 관리할 수 있습니다.
이 패턴을 사용하는 이유는 다음과 같습니다.
- 데이터의 일관성과 시스템 복원력 확보
- 서비스간 느슨한 결합
- 비동기적 확장성
- 시스템 장애발생시 복구능력
- Exactly Once 처리
- 비즈니스 로직과 Kafka 이벤트 발행 분리
- 데이터 일관성 유지 및 재발행 관리
Idempotent Consumer/Producer 에 대한 자세한 원리는 아래 블로그를 확인하면 많이 도움이 됩니다.
-
Consumer에서 중복처리를 방지합니다.
-
같은 메시지를 여러번 읽더라도 동일한 결과를 만들어내도록 처리하는 Consumer를 의미합니다. Idempotent Consumer 은 같은 메시지를 여러번 소비하는 문제(메시지 중복 처리)를 해결합니다.
-
처리한 메시지의 ID를 DB에 저장하고, 이미 처리된 메시지들은 무시하여 처리이력을 저장합니다.
-
중복메시지가 저장되지 않기위하여 Unique 제약조건을 설정하여 메시지에 고유식별자를 부여합니다.
-
예를 들면 동일한 예약ID가 있으면 처리하지 않도록 해야합니다.
-
Producer 측에서 중복저장을 방지합니다.
-
Idempotent Producer 는 Kafka에서 같은 메시지를 여러번 보내더라도, Broker가 딱 한번만 저장되도록 보장하는 전략을 의미합니다.
-
네트워크 문제, 타임아웃 등으로 Producer가 재시도(retry)를 하게되는데, 이때 같은 메시지가 Kafka에 여러번 저장될 위험이 있습니다. 중복으로 메시지가 전달되어 중복으로 저장될 가능성이 있는 이유는 Kafka는 기본적으로 At Least Once (최소 한번 혹은 두번 이상 메시지 전달) 방식으로 전송을 하기 때문입니다.
-
Kafka에서는 Producer가 메시지를 전송하려고 할때,
Producer의 고유 ID
와sequencee number
,epoch
정보를 함께 보냅니다.-
Producer의 고유 ID
: kafka가 부여하는 Producer들을 구분하기 위한 식별자 입니다. -
sequence number
: Producer가 특정 Partition에 보낸 메시지의 순서번호를 의미합니다.- 단, Partition의 Offset과 전혀 다른 개념이며, Offset은 Broker가 Partition에 메시지를 저장할때 부여하는 고유번호입니다.
-
epoch
: 재시작 구분자 입니다.
-
- 이벤트 손실 문제
- 애플리케이션 장애시 메모리 기반 이벤트(SpringApplicationEvent) 유실
- 트랜잭션 커밋후 이벤트 발행전 시스템 다운
- 중복처리 문제
- 네트워크 장애로 인한 이벤트 재전송
- 같은 이벤트를 여러번 처리로 인한 데이터 중복
- 순서보장 문제
- 동일 사용자의 여러 이벤트가 순서대로 처리되지 않는 문제 발생
- 부분실패 문제
- 일부 이벤트 리스너만 실패하여 데이터 불일치 발생
- 장애전파 문제
- 하나의 서비스 장애가 전체 시스템에 영향
- 확장성 문제
- 특정 이벤트 처리량 증가와 병목 발생
- 동기적 처리로 인한 성능 저하
- 모니터링 실패 문제
- 분산된 이벤트 처리 상태 추적이 어려움.
- 어느단계에서 실패했는지 디버깅이 어려움.
- 스키마 진화 문제
- 이벤트 구조 변경시 호환성 문제 발생
- 새로운 필드 추가나 변경이 기존 컨슈머에 영향
- 이벤트 손실 문제
-
SpringApplicationEvent와 Kafka의 비교
항목 SpringApplicationEvent Kafka 저장위치 JVM 메모리 디스크(영구저장) 장애복구 불가능(이벤트소실) 가능(복제본존재) 확장성 단일인스턴스만 멀티 인스턴스 가능 순서보장 어려움 Partition 기반 보장 재처리 불가능 Offset 기반 재처리 모니터링 제한적 풍부한 메트릭 -
Kafka Transactions: Producer와 Consumer를 하나의 트랜잭션으로 묶어서 Exactly Once 처리보장
-
Replication: 여러 Broker에 데이터 복제로 장애시에도 데이터 보존
-
Persistent Storage: 디스크기반 저장으로 메모리기반 이벤트 시스템의 휘발성 문제 해결
-
Producer Acknowledgment:
acks=all
설정으로 모든 replica가 받을 때까지 확인
- 중복처리 문제
- Idempotent Producer: 중복메시지 발행 방지
- Consumer Offset Management: 수동커밋으로 처리완료후에만 오프셋 커밋
- Transactional Consumer: 트랜잭션 기반으로 consume-transform-produce 패턴에서 중복방지
- 순서보장 문제
- Partition Key: 동일한 키를 가진 메서드들은 같은 파티션으로 보내져서 순서 보장.
- Single Partition Ordering: 파티션내에서는 메시지 순서가 보장됨.
- Consumer Concurrency Control: 파티션별로 단일 컨슈머 스레드 할당으로 순차처리
- 부분실패 문제
- Multiple Consumer Groups: 각 서비스마다 독립적인 컨슈머 그룹으로 격리된 처리
- Dead Letter Topic(DLT): 처리 실패한 메시지를 별도 토픽으로 라우팅하여 후속처리
- 장애전파 문제
- Consumer Group Independence: 각 컨슈머 그룹이 독립적으로 동작하여 장애 격리
- Automatic Retry: 컨슈머 레벨에서 자동 재시도 메커니즘
- Circuit Breaker Pattern: Kafka Connect의 에러핸들링으로 회로 차단기 패턴 구현
- 확장성 문제
- Horizontal Scaling: 파티션수 증가와 컨슈머 인스턴스 추가로 수평확장
- Batch Processing: 배치 단위 메시지 처리로 처리량 향상
- 모니터링 실패 문제
- Kafka Streams: 실시간 스트림 처리로 매트릭 생성 및 모니터링
- Kafka Connect Monitoring: 커넥터 상태 밍 에러로그 모니터링
- 스키마 진화 문제
- Schema Registry: 스키마 버젼 관리 및 호환성 검증
- Schema Validation: Producer/Consumer 에서 스키마 검증 자동화
플로우 차트
graph TB
subgraph "API Layer"
A[사용자 결제 요청]
B[Payment Controller]
end
subgraph "Core Domain"
C[Payment Service]
D[임시 결제 생성]
end
subgraph "Saga Orchestrator"
E[Payment Saga Orchestrator]
F[Saga State Management]
end
subgraph "Kafka Topics"
G[Request Topics]
H[Response Topics]
I[Completion Topics]
J[Compensation Topics]
end
subgraph "Domain Services"
K[Point Service]
L[Concert Service]
M[Notification Service]
N[Compensation Handler]
end
%% Main Flow - Simplified
A --> B
B --> C
C --> D
D --> E
%% Saga to Kafka
E --> G
%% Kafka to Services
G --> K
G --> L
%% Services Response
K --> H
L --> H
%% Response to Saga
H --> E
%% Completion Flow
E --> I
I --> M
%% Failure Flow
E -.-> J
J -.-> N
%% State Management
E <--> F
%% Styling
classDef api fill:#e3f2fd
classDef core fill:#f3e5f5
classDef saga fill:#ffebee
classDef kafka fill:#fff3e0
classDef service fill:#e8f5e8
class A,B api
class C,D core
class E,F saga
class G,H,I,J kafka
class K,L,M,N service
성공 플로우
sequenceDiagram
participant User as 🧑💼 사용자
participant Payment as 💳 Payment Service
participant Saga as 🎭 Saga Orchestrator
participant Kafka as 📨 Kafka Topics
participant Point as 💰 Point Service
participant Concert as 🎵 Concert Service
participant Notification as 📱 Notification Service
Note over User, Notification: ✅ 성공 플로우
rect rgba(99, 146, 234, 0.1)
Note over User, Saga: Phase 1: 결제 요청 및 초기 검증
User->>+Payment: 1. 결제 요청 (동기)
Payment->>Payment: 2. 기본 검증<br/>(예약 데이터 조회, 기본 잔액 확인)
Payment->>+Saga: 3. 결제 사가 시작 요청
end
rect rgba(255, 152, 0, 0.1)
Note over Saga, Concert: Phase 2: 분산 트랜잭션 실행
Saga->>Kafka: 4. point-deduction-requested 이벤트 발행
Kafka->>+Point: 포인트 차감 요청
Point->>Point: 포인트 차감 처리
Point->>Kafka: 5. point-deduction-completed 응답
Kafka->>Saga: 포인트 차감 성공 알림
Saga->>Kafka: 6. concert-confirmation-requested 이벤트 발행
Kafka->>+Concert: 콘서트 예약 확정 요청
Concert->>Concert: 예약 확정 처리
Concert->>Kafka: 7. concert-confirmation-completed 응답
Kafka->>Saga: 예약 확정 성공 알림
end
rect rgba(76, 175, 80, 0.1)
Note over Saga, Notification: Phase 3: 최종 결제 완료
Saga->>Saga: 8. 모든 단계 성공 확인
Saga->>Kafka: 9. payment-completed 이벤트 발행
Kafka->>+Notification: 성공 알림 발송 요청
Notification->>Notification: 10. 성공 알림 발송
Saga-->>Payment: 사가 완료 알림
Payment-->>-User: 11. 결제 완료 응답
deactivate Point
deactivate Concert
deactivate Notification
deactivate Saga
end
실패 플로우
sequenceDiagram
participant User as 🧑💼 사용자
participant Payment as 💳 Payment Service
participant Saga as 🎭 Saga Orchestrator
participant Kafka as 📨 Kafka Topics
participant Point as 💰 Point Service
participant Concert as 🎵 Concert Service
participant Notification as 📱 Notification Service
participant Compensation as 🔄 Compensation Handler
Note over User, Compensation: ❌ 실패 플로우 (콘서트 매진 시나리오)
rect rgba(99, 146, 234, 0.1)
Note over User, Saga: 초기 단계 (성공 플로우와 동일)
User->>+Payment: 1-3. 결제 요청 → 사가 시작
Payment->>+Saga: 사가 시작 요청
end
rect rgba(76, 175, 80, 0.1)
Note over Saga, Point: 포인트 차감 성공
Saga->>Kafka: 4. point-deduction-requested
Kafka->>+Point: 포인트 차감 요청
Point->>Point: 5. 포인트 차감 처리 성공
Point->>Kafka: point-deduction-completed
Kafka->>Saga: 포인트 차감 성공 알림
end
rect rgba(244, 67, 54, 0.1)
Note over Saga, Concert: 콘서트 예약 실패
Saga->>Kafka: 6. concert-confirmation-requested
Kafka->>+Concert: 콘서트 예약 확정 요청
Concert->>Concert: ❌ 콘서트 매진 확인
Concert->>Kafka: concert-confirmation-failed
Kafka->>Saga: 7. 예약 확정 실패 알림
end
rect rgba(244, 67, 54, 0.1)
Note over Saga, Compensation: 보상 트랜잭션 실행
Saga->>Saga: 8. 실패 감지 및 보상 시작
Saga->>Kafka: 9. compensation-events 발행<br/>(포인트 복구 요청)
Kafka->>+Compensation: 보상 트랜잭션 요청
Compensation->>Point: 10. 차감된 포인트 복구
Point->>Point: 포인트 복구 처리
Point->>Compensation: 복구 완료
Compensation->>Kafka: compensation-completed
Kafka->>+Notification: 실패 알림 발송 요청
Notification->>Notification: 11. 실패 알림 발송 (매진 안내)
Saga-->>Payment: 사가 실패 완료
Payment-->>-User: 12. 결제 실패 응답 (매진 안내)
deactivate Point
deactivate Concert
deactivate Notification
deactivate Compensation
deactivate Saga
end
보상트랜잭션 플로우
sequenceDiagram
participant Saga as 🎭 Saga Orchestrator
participant SagaState as 📊 Saga State Manager
participant Kafka as 📨 Kafka Topics
participant Point as 💰 Point Service
participant Concert as 🎵 Concert Service
participant Compensation as 🔄 Compensation Handler
participant DLT as 🚨 Dead Letter Topic
Note over Saga, DLT: 🔄 보상 트랜잭션 상세 플로우
rect rgba(244, 67, 54, 0.1)
Note over Saga, SagaState: 1. 실패 상황 분석
Saga->>+SagaState: 실패 지점 조회
SagaState-->>Saga: 완료된 작업 목록 반환<br/>✅ 포인트 차감 완료<br/>❌ 콘서트 예약 실패
end
rect rgba(255, 193, 7, 0.1)
Note over Saga, Compensation: 2. 보상 트랜잭션 시작
Saga->>Kafka: compensation-events 발행
Note over Kafka: 보상 이벤트 내용:<br/>- sagaId<br/>- 보상할 작업: POINT_RESTORE<br/>- 원본 데이터
Kafka->>+Compensation: 보상 요청 수신
end
rect rgba(255, 193, 7, 0.1)
Note over Compensation, Point: 3. 역순 보상 실행
Compensation->>+Point: 포인트 복구 요청<br/>(멱등성 키 포함)
alt 포인트 복구 성공
Point->>Point: 💰 포인트 복구 처리
Point-->>Compensation: 복구 성공 응답
Compensation->>Kafka: compensation-completed 발행
else 포인트 복구 실패 (시스템 오류)
Point-->>Compensation: 복구 실패 응답
Compensation->>Kafka: compensation-failed 발행
Kafka->>+DLT: 보상 실패 이벤트 저장
Note over DLT: 수동 처리 필요<br/>운영팀 알림 발송
end
end
rect rgba(76, 175, 80, 0.1)
Note over Saga, SagaState: 4. 최종 상태 업데이트
Kafka->>Saga: 보상 완료 알림
Saga->>SagaState: 사가 상태 업데이트<br/>(COMPENSATED)
SagaState-->>Saga: 상태 저장 완료
deactivate Point
deactivate Compensation
deactivate SagaState
end
Note over Saga, DLT: 💡 핵심 보장 사항
Note over Saga, DLT: • 멱등성: 동일 보상 요청 여러 번 실행 안전
Note over Saga, DLT: • 원자성: 각 보상 작업은 완전 성공 또는 실패
Note over Saga, DLT: • 추적성: 모든 보상 작업 이력 보존
💻 Broker & Replication & ISR
- Broker : 2
- Replication : 2
- 최소 In-Sync-Replicas(ISR) : 2
🎯 Producers
번호 | Producer 이름 | 역할 | 발행 토픽 |
---|---|---|---|
1 | Payment Saga Orchestrator | 분산 트랜잭션 단계별 이벤트 발행 |
point-deduction-requested concert-confirmation-requested payment-completed compensation-events
|
2 | Point Service | 포인트 처리 결과 이벤트 발행 |
point-deduction-completed point-deduction-failed
|
3 | Concert Service | 콘서트 예약 처리 결과 이벤트 발행 |
concert-confirmation-completed concert-confirmation-failed
|
4 | Compensation Handler | 보상 트랜잭션 결과 이벤트 발행 | compensation-completed |
- 📊 Producer별 토픽 발행 현황
Producer | 발행 토픽 수 | 주요 발행 유형 | 발행 특성 |
---|---|---|---|
Payment Saga Orchestrator | 4개 | 트랜잭션 제어 이벤트 | 순차적 단계별 발행 |
Point Service | 2개 | 처리 결과 이벤트 | 성공/실패 상태 응답 |
Concert Service | 2개 | 처리 결과 이벤트 | 성공/실패 상태 응답 |
Compensation Handler | 1개 | 보상 완료 이벤트 | 보상 트랜잭션 결과 |
- 🔄 Producer와 Topic 매핑 관계
Topic | 발행하는 Producer | 발행 목적 |
---|---|---|
point-deduction-requested |
Payment Saga Orchestrator | 포인트 차감 작업 시작 |
concert-confirmation-requested |
Payment Saga Orchestrator | 예약 확정 작업 시작 |
payment-completed |
Payment Saga Orchestrator | 전체 결제 성공 알림 |
compensation-events |
Payment Saga Orchestrator | 보상 트랜잭션 시작 |
point-deduction-completed |
Point Service | 포인트 차감 성공 응답 |
point-deduction-failed |
Point Service | 포인트 차감 실패 응답 |
concert-confirmation-completed |
Concert Service | 예약 확정 성공 응답 |
concert-confirmation-failed |
Concert Service | 예약 확정 실패 응답 |
compensation-completed |
Compensation Handler | 보상 트랜잭션 완료 알림 |
- 📈 Producer 역할별 분류
분류 | Producer | 특징 |
---|---|---|
오케스트레이터 | Payment Saga Orchestrator | 전체 트랜잭션 흐름 제어 |
비즈니스 서비스 | Point Service Concert Service |
각 도메인의 비즈니스 로직 처리 결과 발행 |
보상 처리기 | Compensation Handler | 실패 상황에서 보상 작업 결과 발행 |
🗓️ Topics
번호 | Topic 이름 | 설명 | 파티션 수 | 용도 |
---|---|---|---|---|
1 | point-deduction-requested |
포인트 차감 요청 | 5개 | 핵심 비즈니스 토픽 |
2 | point-deduction-completed |
포인트 차감 완료 | 3개 | 상태 응답 토픽 |
3 | point-deduction-failed |
포인트 차감 실패 | 2개 | 실패 처리 토픽 |
4 | concert-confirmation-requested |
콘서트 예약 확정 요청 | 5개 | 핵심 비즈니스 토픽 |
5 | concert-confirmation-completed |
콘서트 예약 확정 완료 | 3개 | 상태 응답 토픽 |
6 | concert-confirmation-failed |
콘서트 예약 확정 실패 | 2개 | 실패 처리 토픽 |
7 | payment-completed |
전체 결제 완료 | 3개 | 완료 알림 토픽 |
8 | compensation-events |
보상 트랜잭션 이벤트 | 2개 | 보상 처리 토픽 |
9 | saga-state-events |
Saga 상태 관리 이벤트 | 1개 | 상태 관리 토픽 |
10 | notification-events |
알림 발송 이벤트 | 3개 | 알림 처리 토픽 |
11 | payment-dlt |
Dead Letter Topic | 1개 | 실패 이벤트 저장 |
👥 Consumers
번호 | Consumer 이름 | Consumer Group | 구독 토픽 | 역할 |
---|---|---|---|---|
1 | Point Service Consumer | point-service-group |
point-deduction-requested compensation-events
|
포인트 차감/복구 처리 |
2 | Concert Service Consumer | concert-service-group |
concert-confirmation-requested compensation-events
|
콘서트 예약 확정/취소 처리 |
3 | Saga Orchestrator Consumer | saga-orchestrator-group |
point-deduction-completed point-deduction-failed concert-confirmation-completed concert-confirmation-failed
|
분산 트랜잭션 오케스트레이션 |
4 | Notification Service Consumer | notification-service-group |
payment-completed payment-failed notification-events
|
사용자 알림 발송 |
5 | Saga State Manager Consumer | saga-state-group |
saga-state-events |
Saga 상태 저장/조회 |
6 | DLT Handler Consumer | dlt-handler-group |
payment-dlt |
실패 이벤트 모니터링/처리 |
- 📋 Consumer Group별 토픽 구독 현황
Consumer Group | 구독 토픽 수 | 주요 처리 유형 | 처리 특성 |
---|---|---|---|
point-service-group |
2개 | 포인트 관련 작업 | 사용자별 순서 보장 필요 |
concert-service-group |
2개 | 콘서트 예약 작업 | 사용자별 순서 보장 필요 |
saga-orchestrator-group |
4개 | 상태 응답 처리 | Saga별 순서 보장 필요 |
notification-service-group |
3개 | 알림 발송 | 빠른 처리, 순서 무관 |
saga-state-group |
1개 | 상태 관리 | 전체 순서 보장 필요 |
dlt-handler-group |
1개 | 실패 처리 | 순차적 분석/처리 |
- 🔄 Consumer와 Topic 매핑 관계
Topic | 구독하는 Consumer Group | 처리 목적 |
---|---|---|
point-deduction-requested |
point-service-group |
포인트 차감 실행 |
concert-confirmation-requested |
concert-service-group |
예약 확정 실행 |
compensation-events |
point-service-group concert-service-group
|
보상 트랜잭션 실행 |
point-deduction-completed |
saga-orchestrator-group |
Saga 진행 상태 업데이트 |
point-deduction-failed |
saga-orchestrator-group |
Saga 실패 처리 |
concert-confirmation-completed |
saga-orchestrator-group |
Saga 진행 상태 업데이트 |
concert-confirmation-failed |
saga-orchestrator-group |
Saga 실패 처리 |
payment-completed |
notification-service-group |
성공 알림 발송 |
payment-failed |
notification-service-group |
실패 알림 발송 |
notification-events |
notification-service-group |
기타 알림 발송 |
saga-state-events |
saga-state-group |
Saga 상태 저장 |
payment-dlt |
dlt-handler-group |
실패 이벤트 처리 |
Apache Kafka에 대한 개념을 학습하고, 콘서트시나리오에서 EDA(Event Driven Architecture)을 적용했을 때의 한계를 Kafka 도입 및 설계 를 통해 이벤트 기반의 분산된 환경을 개선할 수 있게 되었습니다.
참고자료
- Apache kafka intro
- DEVOCEAN - Consumer 내부동작 원리와 구현
- Spring Docs - DLT Processing
- Microservice Architecture - Transactional Outbox Pattern
- conduktor kafkademy - idempotent kafka producer
- Confluent - Apache Kafka Performance
- NetApp Instaclustr - kafka architecture
- Idempotent Producer and Consumer
- 우아콘2022 - 회원시스템 이벤트기반 아키텍쳐 구축하기
- Kafka Producer와 Consumer 동작원리와 고가용성
- Kakao Tech Meet 2023 - 신뢰성있는 카프카 애플리케이션을 만드는 3가지 방법
- Microservice Architecture - Idempotent Consumer
- Microservice Architecture - Handling duplicate messages using the Idempotent consumer pattern
- Kafka에서 파티션 증가 없이 동시 처리량을 늘리는 방법 - Parallel Consumer
- 우아한 기술블로그 - 우리팀은 카프카를 어떻게 사용하고 있을까
- Kafka 메시지 전송보장 방식 알아보기(At Most Once, At Least Once, Exactly Once)
- Baeldung - Kafka Spring Dead Letter Queue
- kakao techmeet2023 - 신뢰성있는 카프카 애플리케이션 만드는 3가지 방법(최원영 cory)