Apache Kafka
는 분산 메시징 플랫폼
으로, 데이터를 생산자(Producer)
에서 소비자(Consumer)
로 빠르고 안정적으로 전달하기 위해 설계된 시스템입니다. 처음 LinkedIn에서 개발되었으며, 현재는 오픈소스로 전 세계적으로 사용되고 있습니다. Kafka는 단순한 메시징 큐
이상의 기능을 제공하며, 대규모 실시간 데이터 스트리밍
및 분산 처리
를 가능하게 합니다.
1. 🌐 Kafka 개요
Kafka는 Publish-Subscribe 모델
기반으로 동작하며, 다양한 데이터 소스에서 발생하는 데이터를 처리하는 데 사용됩니다. 이를 통해 실시간 데이터 스트림
을 관리하거나, 데이터를 저장소로 전송하는 데이터 파이프라인
역할을 수행합니다.
🧩 Kafka의 주요 구성 요소
- 📤 Producer (생산자): 데이터를 생성하고 Kafka의 특정
토픽(Topic)
으로 전송합니다. - 📦 Broker (브로커): 데이터를 저장하고 관리하며, 여러 브로커가
클러스터
를 형성하여 데이터를분산 처리
합니다. - 📥 Consumer (소비자): 데이터를 읽고 처리하는 애플리케이션입니다.
- 🗂 Topic (토픽): 데이터를 분류하기 위한 논리적인 단위입니다. 각 토픽은 여러 개의
Partition(파티션)
으로 나뉩니다. - 🔀 Partition (파티션): 데이터를 물리적으로 나누어 저장하는 단위입니다.
- 🛠 ZooKeeper/Kafka Raft: 클러스터 상태와 노드 메타데이터를 관리합니다. (최신 Kafka 버전에서는
Raft
로ZooKeeper
를 대체)
🔑 Kafka의 핵심 개념
- 🌍 분산 아키텍처:
Kafka는분산 시스템
으로 설계되어 높은 가용성과 확장성을 제공합니다. 데이터를 여러 노드에 분산 저장하여 성능을 최적화하고 장애에도 대비합니다. - 💾 내구성(Durability):
Kafka는 데이터를디스크에 저장
하며, 데이터를 복제하여 안정성을 유지합니다. 데이터 손실을 최소화하고 필요할 경우 재처리를 지원합니다. - 🔢 오프셋(Offset):
Kafka는 데이터를 순차적으로 처리하기 위해오프셋
개념을 사용합니다. 소비자는 오프셋을 기준으로 데이터를 읽으며, 원하는 시점으로 돌아가 데이터를 다시 처리할 수 있습니다.
2. 🔄 Kafka 데이터 처리 방식
Kafka의 데이터 처리 방식은 생산자 → 브로커 → 소비자
의 흐름으로 이루어집니다. 각 단계에서 특정한 기능과 특징이 적용됩니다.
1️⃣ 📤 데이터 생성: Producer
생산자
는 데이터를 생성한 후 Kafka의 특정토픽
으로 데이터를 보냅니다.- 데이터를 보낼 때,
Round-Robin 방식
또는Key 기반 해싱
으로 특정Partition
에 데이터가 저장됩니다. - Kafka는 프로듀서가 보낸 데이터를
배치 처리
하여 네트워크 효율성을 높입니다.
2️⃣ 💾 데이터 저장: Broker
- Kafka의 브로커는 데이터를
Partition
단위로 저장합니다. - 각 Partition은 여러 브로커에
복제
되어 안정성을 높입니다.- 예: 복제 계수(Replication Factor)가 3인 경우, 동일한 데이터가 3개의 브로커에 저장됩니다.
- Kafka는 데이터를 디스크에 저장하지만, 데이터 쓰기 및 읽기가 매우 빠릅니다. 이는
순차적 I/O
를 활용하기 때문입니다.
3️⃣ 📥 데이터 소비: Consumer
소비자
는 Kafka에서 데이터를 읽어오는 역할을 합니다.- 소비자는 각
Partition
에서 데이터를오프셋 기반
으로 읽습니다. 이렇게 하면 데이터를실시간
으로 처리하거나, 과거 데이터를 다시 읽는재처리
가 가능합니다. 소비자 그룹 (Consumer Group):
- 여러 소비자가 그룹을 이루어 데이터를 처리할 수 있습니다.
- 각 소비자는 Partition을 독점적으로 처리하며, 부하가 분산됩니다.
🔍 Kafka 데이터 처리의 특징
- 🛠 배치 처리와 스트리밍 처리의 균형:
- Kafka는 데이터를 실시간으로 처리하면서도, 데이터의 배치 처리 및 재처리를 지원합니다.
- 📥 Pull 기반 처리:
- 소비자가 데이터를
요청(Pull)
하여 가져오는 방식입니다. 이는 RabbitMQ 같은Push 기반
메시징 시스템과의 주요 차이점입니다.
- 소비자가 데이터를
- 🗄 데이터 보관:
- Kafka는 데이터를
로그 파일
형태로 저장하며, 설정에 따라 특정 기간 동안 데이터를 유지합니다.
- Kafka는 데이터를
3. 🚀 Kafka의 성능
Kafka는 대규모 데이터를 빠르고 안정적으로 처리할 수 있도록 설계되었습니다. 아래는 Kafka 성능의 주요 특징입니다:
1️⃣ ⚡ 처리량
- Kafka는
초당 수백만 건의 메시지
를 처리할 수 있습니다. - 데이터 처리량은
파티션 수
와브로커 수
에 따라 확장 가능하며, 클러스터의 크기를 조정하여 성능을 높일 수 있습니다.
2️⃣ ⏱ 낮은 지연 시간
- Kafka는
밀리초(ms)
단위의 지연 시간으로 데이터를 처리합니다. - 실시간 데이터 처리에 적합하며, 실시간 분석, IoT 애플리케이션, 로그 처리에 널리 사용됩니다.
3️⃣ 📈 확장성
- Kafka는 새로운 브로커를 추가함으로써
클러스터 크기
를 동적으로 확장할 수 있습니다. - 각 브로커가 독립적으로 동작하므로, 장애가 발생해도 전체 시스템에 영향을 주지 않습니다.
4️⃣ 💾 내구성
- Kafka는 데이터를
디스크에 저장
하며, 데이터 손실을 방지하기 위해복제
합니다. - 장애 복구 시 복제된 데이터를 기반으로 복원이 가능합니다.
5️⃣ 🔒 안정성
- Kafka는 분산 환경에서 노드 간 통신을 효율적으로 관리하여
안정적인 메시징
을 제공합니다. - 클러스터를 통해 데이터가 특정 노드에만 의존하지 않도록 설계되어 있습니다.
4. ⚖️ RabbitMQ와의 차이점
Kafka와 RabbitMQ는 모두 메시징 시스템이지만, 설계 철학과 사용 사례에서 큰 차이가 있습니다.
특징Kafka
RabbitMQ
아키텍처
분산 시스템, 브로커 기반중앙 집중형 메시징 큐메시징 모델
Publish-Subscribe메시지 큐 (Queue)데이터 전달 방식
Pull 기반Push 기반데이터 저장
디스크 기반 (로그 보관 가능)메모리 우선 (빠른 처리)복잡성
클러스터 설정 및 운영이 복잡상대적으로 간단한 설정실시간성
밀리초 수준 (약간의 지연)매우 낮은 지연 시간 (실시간 처리)확장성
브로커와 Partition 추가로 높은 확장성확장성은 제한적사용 사례
대규모 데이터 처리, 로그 분석실시간 알림, 요청-응답 구조
📌 RabbitMQ가 더 적합한 경우
실시간 처리
가 중요한 경우 (예: 알림 서비스, 요청-응답)소규모 메시징
환경에서 단순하고 빠른 설정이 필요한 경우
📌 Kafka가 더 적합한 경우
대규모 데이터 스트리밍
이 필요한 경우 (예: 로그 수집, 데이터 파이프라인)높은 내구성
과 데이터 보존이 중요한 경우
5. 🚀 Kafka를 활용한 시스템 설계(결제 시스템)
🎯 결제 시스템 설계 목표
- 확장성
- 대량의 주문 요청에도 안정적으로 처리.
- 데이터 신뢰성
- 결제 및 주문 데이터 유실 방지.
- 서비스 분리
- 재고 관리, 결제, 알림 등 서비스 간 독립적이고 느슨한 결합.
- 오류 처리 및 복구
- 실패 시 재처리(재시도) 및 데드 레터 큐(Dead Letter Queue) 사용.
- 실시간 피드백
- 사용자에게 즉각적인 상태 알림 제공.
🛠️ Kafka와 Spring Boot로 설계한 시스템 구조
1. 사용자 → 2. 주문 서비스(Order Service) → 3. Kafka (`order-created` 이벤트)
↓ ↘
4. 재고 서비스(Inventory Service) 6. 결제 서비스(Payment Service)
↓ ↘
5. Kafka (`inventory-confirmed` or 7. Kafka (`payment-success` or
`inventory-failed` 이벤트) `payment-failure` 이벤트)
↓ ↘
8. 알림 서비스(Notification Service) ← 9. Kafka
🧩 단계별 구현 및 실패 처리 전략
1️⃣ 주문 생성 (Order Create)
- 동작: 사용자의 요청을 받아 주문 정보를 저장하고 Kafka에
order-created
이벤트를 발행합니다. - 오류 처리:
- Kafka로 메시지 발행 실패 시 재시도 로직 추가.
- DB 트랜잭션과 Kafka 발행 간 트랜잭션 동기화(e.g., Kafka Transactional API) 구현.
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private KafkaTemplate<String, Order> kafkaTemplate;
@PostMapping
public ResponseEntity<String> createOrder(@RequestBody Order order) {
try {
// 주문 저장 (DB)
orderService.saveOrder(order);
// Kafka에 이벤트 발행
kafkaTemplate.send("order-created", order);
return ResponseEntity.ok("Order Created Successfully!");
} catch (Exception e) {
// 예외 처리: Kafka 발행 실패 시 로깅 및 재시도
log.error("Failed to create order: ", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to create order");
}
}
}
2️⃣ 재고 확인 (Inventory Check)
- 동작:
order-created
이벤트를 구독하고 재고를 확인합니다. 결과에 따라inventory-confirmed
또는inventory-failed
이벤트를 발행합니다. - 오류 처리:
- 재고 확인 실패: Kafka 재시도를 설정하거나, 실패 메시지를 데드 레터 큐(Dead Letter Queue)로 전송.
- 재시도: 특정 횟수(예: 3회) 재시도 후 실패하면 알림 서비스로 전달.
@KafkaListener(topics = "order-created", groupId = "inventory-service")
public void checkInventory(Order order) {
try {
boolean inventoryAvailable = inventoryService.checkInventory(order.getItems());
if (inventoryAvailable) {
kafkaTemplate.send("inventory-confirmed", order);
} else {
kafkaTemplate.send("inventory-failed", order);
}
} catch (Exception e) {
// 실패 처리 로직
log.error("Inventory check failed for order: " + order.getOrderId(), e);
// 데드 레터 큐로 전송
kafkaTemplate.send("inventory-dlq", order);
}
}
3️⃣ 결제 처리 (Payment Process)
- 동작:
inventory-confirmed
이벤트를 구독하여 결제를 처리하고 결과를 발행합니다. - 오류 처리:
- 결제 실패: Kafka 이벤트로
payment-failure
발행 후 알림 서비스에서 처리. - 결제 시스템 장애: Kafka 재시도 설정으로 복구 시도.
- 결제 실패: Kafka 이벤트로
@KafkaListener(topics = "inventory-confirmed", groupId = "payment-service")
public void processPayment(Order order) {
try {
boolean paymentSuccess = paymentService.process(order);
if (paymentSuccess) {
kafkaTemplate.send("payment-success", order);
} else {
kafkaTemplate.send("payment-failure", order);
}
} catch (Exception e) {
// 예외 발생 시 재시도 또는 데드 레터 큐 처리
log.error("Payment processing failed for order: " + order.getOrderId(), e);
kafkaTemplate.send("payment-dlq", order);
}
}
4️⃣ 사용자 알림 (Notification)
- 동작:
payment-success
또는payment-failure
이벤트를 구독하여 사용자에게 알림(SMS, Email 등)을 보냅니다. - 오류 처리:
- 알림 실패 시 실패 내역을 기록하고 관리자에게 보고.
@KafkaListener(topics = {"payment-success", "payment-failure"}, groupId = "notification-service")
public void sendNotification(Order order) {
try {
if ("payment-success".equals(order.getStatus())) {
notificationService.sendSuccessNotification(order);
} else {
notificationService.sendFailureNotification(order);
}
} catch (Exception e) {
// 알림 실패 처리
log.error("Notification failed for order: " + order.getOrderId(), e);
}
}
🛡️ 실패 처리 전략
- Kafka 재시도
- Spring Kafka의
RetryTemplate
을 사용하여 메시지 재처리. - 설정 예시:
- Spring Kafka의
spring:
kafka:
# Kafka 브로커 주소 설정 (클러스터의 여러 브로커를 쉼표로 구분하여 추가 가능)
producer:
bootstrap-servers: localhost:9092
# 메시지 전송 보장 수준 설정
# all: 모든 복제본(ISR)에 저장 완료 후 응답 (최고의 신뢰성)
# 1: 리더 브로커만 저장 후 응답 (성능 향상, 일부 유실 가능성)
# 0: 브로커 응답을 기다리지 않음 (유실 가능성 높음, 매우 빠름)
acks: all
# 전송 실패 시 재시도 횟수 (일시적 네트워크 장애 등 대응)
retries: 5
# Producer가 메시지를 일정 크기(16KB)까지 모아서 전송하여 성능 최적화
batch-size: 16384
# 메시지를 전송하기 전 대기하는 시간 (단위: ms, 대기 후 배치 전송)
linger.ms: 5
# 메시지 버퍼 크기 (Producer가 메시지를 전송하기 전까지 보관할 메모리 크기)
buffer-memory: 33554432
# 메시지 중복 방지를 위해 동시에 처리할 수 있는 최대 요청 수
max-in-flight-requests-per-connection: 5
# Producer에서 메시지를 직렬화할 때 사용할 Serializer (String 기반)
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer:
# 자동 커밋을 비활성화하여, 수동으로 메시지를 확인 후 커밋 (데이터 유실 방지)
enable-auto-commit: false
# Consumer가 새 컨슈머 그룹으로 시작할 때 어디서부터 데이터를 읽을지 지정
# earliest: 가장 오래된 메시지부터 소비
# latest: 가장 최근의 메시지부터 소비
auto-offset-reset: earliest
# 한 번에 가져올 최대 메시지 수 (너무 크면 메모리 부족 위험)
max-poll-records: 100
# 최소 가져올 데이터 크기 (50KB 이상의 데이터가 쌓일 때 가져오도록 설정)
fetch-min-size: 50000
# 데이터를 기다리는 최대 시간 (단위: ms, 적절한 값 조정 필요)
fetch-max-wait: 500
# 컨슈머가 브로커에게 보내는 하트비트 간격 (단위: ms)
heartbeat-interval: 3000
# 컨슈머가 일정 시간 내에 하트비트를 보내지 않으면 그룹에서 제거 (단위: ms)
session-timeout-ms: 10000
# Consumer에서 메시지를 역직렬화할 때 사용할 Deserializer (String 기반)
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
listener:
# 컨슈머가 메시지를 배치 단위로 수신하여 처리 (성능 최적화)
type: batch
# 특정 토픽이 존재하지 않을 때 예외를 발생시키지 않도록 설정
missing-topics-fatal: false
# 메시지 처리 실패 시 최대 5번까지 재시도 (오류 복구 로직을 위해 설정)
retry:
max-attempts: 5
# 재시도 간 간격 설정 (1초부터 시작, 실패할 때마다 증가)
backoff:
initial-interval: 1000 # 첫 번째 재시도 간격 (1초)
multiplier: 2.0 # 다음 재시도까지의 시간 배수 증가 (1초 → 2초 → 4초)
max-interval: 10000 # 최대 재시도 간격 (10초)
# 메시지 처리가 완료된 후 수동으로 오프셋을 커밋하도록 설정 (acknowledgment 필요)
ack-mode: manual_immediate
# 메시지 처리 실패 시 Dead Letter Queue(DLQ)에 저장 (재처리 및 분석)
error-handler:
dead-letter-publish: true
- 데드 레터 큐(Dead Letter Queue)
- Listener처리 실패 메시지를 별도의 Kafka 토픽으로 이동.
- 실패한 데이터를 분석 및 복구하는 데 사용.
- Spring Kafka에서 데드 레터 큐를 활성화하려면
error-handling
설정을 추가합니다.
- 트랜잭션 관리
- Kafka 트랜잭션 API를 사용하여 데이터베이스 작업과 Kafka 이벤트 발행을 원자적으로 처리.
- 모니터링 및 로깅
- Kafka와 Spring Boot의 Actuator를 활용한 실시간 모니터링.
- 예외 발생 시 로깅 및 알림을 통해 장애 대응.
- Outbox 패턴
Outbox
패턴은 Kafka와 RDBMS를 함께 사용하는 환경에서 데이터 일관성과 신뢰성을 보장- Kafka 발행 실패와 같은 상황에서 메시지 유실을 방지하며 안정적인 시스템을 설계
오류 처리 핵심: 재시도, 데드 레터 큐, 트랜잭션 관리, 모니터링을 통해 시스템 신뢰성을 극대화합니다.
🎉 정리
Kafka
의 동작 방식:Kafka
는Producer
,Broker
,Consumer
,Topic
,Partition
등으로 구성된분산 메시징 시스템
으로, 데이터를 생성, 저장, 소비하는 과정에서 높은 확장성과 안정성을 제공합니다.Kafka의 장단점
:
Kafka는 대규모 데이터 스트리밍과 내구성이 강점이지만, 설정 및 운영이 복잡하며 실시간 처리가 필요한 경우 적합하지 않을 수 있습니다.RabbitMQ와의 차이점
:RabbitMQ
는 단순하고 빠른 메시징에 적합하며,Kafka
는 대규모 데이터 처리 및 스트리밍에 특화된 플랫폼입니다.
'IT 지식' 카테고리의 다른 글
[Linux] 리눅스 환경에서 sudo 권한 실행 시 비밀번호를 묻지 않도록 설정하는 방법 (0) | 2024.11.25 |
---|---|
WSL에서 Docker 컨테이너 내부에서 외부 호출이 불가능한 문제 해결하기 (0) | 2024.11.21 |
[Jenkins] Jenkins Docker 컨테이너에서 Docker를 사용해 Git 소스 배포하기 (2) | 2024.11.16 |
WSL(Windows Subsystem for Linux)에서 SSH 설정하기 (10) | 2024.11.14 |
[Kubernetes] 쿠버네티스(Kubernetes)란 무엇인가 (2) | 2024.11.12 |