IT 지식

Kafka란 무엇인가?

TaeHuiLee 2025. 1. 18. 03:05
반응형

Apache Kafka분산 메시징 플랫폼으로, 데이터를 생산자(Producer)에서 소비자(Consumer)로 빠르고 안정적으로 전달하기 위해 설계된 시스템입니다. 처음 LinkedIn에서 개발되었으며, 현재는 오픈소스로 전 세계적으로 사용되고 있습니다. Kafka는 단순한 메시징 큐 이상의 기능을 제공하며, 대규모 실시간 데이터 스트리밍분산 처리를 가능하게 합니다.


1. 🌐 Kafka 개요

Kafka는 Publish-Subscribe 모델 기반으로 동작하며, 다양한 데이터 소스에서 발생하는 데이터를 처리하는 데 사용됩니다. 이를 통해 실시간 데이터 스트림을 관리하거나, 데이터를 저장소로 전송하는 데이터 파이프라인 역할을 수행합니다.

🧩 Kafka의 주요 구성 요소

  1. 📤 Producer (생산자): 데이터를 생성하고 Kafka의 특정 토픽(Topic)으로 전송합니다.
  2. 📦 Broker (브로커): 데이터를 저장하고 관리하며, 여러 브로커가 클러스터를 형성하여 데이터를 분산 처리합니다.
  3. 📥 Consumer (소비자): 데이터를 읽고 처리하는 애플리케이션입니다.
  4. 🗂 Topic (토픽): 데이터를 분류하기 위한 논리적인 단위입니다. 각 토픽은 여러 개의 Partition(파티션)으로 나뉩니다.
  5. 🔀 Partition (파티션): 데이터를 물리적으로 나누어 저장하는 단위입니다.
  6. 🛠 ZooKeeper/Kafka Raft: 클러스터 상태와 노드 메타데이터를 관리합니다. (최신 Kafka 버전에서는 RaftZooKeeper를 대체)

🔑 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 데이터 처리의 특징

  1. 🛠 배치 처리와 스트리밍 처리의 균형:
    • Kafka는 데이터를 실시간으로 처리하면서도, 데이터의 배치 처리 및 재처리를 지원합니다.
  2. 📥 Pull 기반 처리:
    • 소비자가 데이터를 요청(Pull) 하여 가져오는 방식입니다. 이는 RabbitMQ 같은 Push 기반 메시징 시스템과의 주요 차이점입니다.
  3. 🗄 데이터 보관:
    • Kafka는 데이터를 로그 파일 형태로 저장하며, 설정에 따라 특정 기간 동안 데이터를 유지합니다.

3. 🚀 Kafka의 성능

Kafka는 대규모 데이터를 빠르고 안정적으로 처리할 수 있도록 설계되었습니다. 아래는 Kafka 성능의 주요 특징입니다:

1️⃣ ⚡ 처리량

  • Kafka는 초당 수백만 건의 메시지를 처리할 수 있습니다.
  • 데이터 처리량은 파티션 수브로커 수에 따라 확장 가능하며, 클러스터의 크기를 조정하여 성능을 높일 수 있습니다.

2️⃣ ⏱ 낮은 지연 시간

  • Kafka는 밀리초(ms) 단위의 지연 시간으로 데이터를 처리합니다.
  • 실시간 데이터 처리에 적합하며, 실시간 분석, IoT 애플리케이션, 로그 처리에 널리 사용됩니다.

3️⃣ 📈 확장성

  • Kafka는 새로운 브로커를 추가함으로써 클러스터 크기를 동적으로 확장할 수 있습니다.
  • 각 브로커가 독립적으로 동작하므로, 장애가 발생해도 전체 시스템에 영향을 주지 않습니다.

4️⃣ 💾 내구성

  • Kafka는 데이터를 디스크에 저장하며, 데이터 손실을 방지하기 위해 복제합니다.
  • 장애 복구 시 복제된 데이터를 기반으로 복원이 가능합니다.

5️⃣ 🔒 안정성

  • Kafka는 분산 환경에서 노드 간 통신을 효율적으로 관리하여 안정적인 메시징을 제공합니다.
  • 클러스터를 통해 데이터가 특정 노드에만 의존하지 않도록 설계되어 있습니다.

4. ⚖️ RabbitMQ와의 차이점

Kafka와 RabbitMQ는 모두 메시징 시스템이지만, 설계 철학과 사용 사례에서 큰 차이가 있습니다.

특징KafkaRabbitMQ아키텍처분산 시스템, 브로커 기반중앙 집중형 메시징 큐메시징 모델Publish-Subscribe메시지 큐 (Queue)데이터 전달 방식Pull 기반Push 기반데이터 저장디스크 기반 (로그 보관 가능)메모리 우선 (빠른 처리)복잡성클러스터 설정 및 운영이 복잡상대적으로 간단한 설정실시간성밀리초 수준 (약간의 지연)매우 낮은 지연 시간 (실시간 처리)확장성브로커와 Partition 추가로 높은 확장성확장성은 제한적사용 사례대규모 데이터 처리, 로그 분석실시간 알림, 요청-응답 구조


📌 RabbitMQ가 더 적합한 경우

  • 실시간 처리가 중요한 경우 (예: 알림 서비스, 요청-응답)
  • 소규모 메시징 환경에서 단순하고 빠른 설정이 필요한 경우

📌 Kafka가 더 적합한 경우

  • 대규모 데이터 스트리밍이 필요한 경우 (예: 로그 수집, 데이터 파이프라인)
  • 높은 내구성과 데이터 보존이 중요한 경우

5. 🚀 Kafka를 활용한 시스템 설계(결제 시스템)

🎯 결제 시스템 설계 목표

  1. 확장성
    • 대량의 주문 요청에도 안정적으로 처리.
  2. 데이터 신뢰성
    • 결제 및 주문 데이터 유실 방지.
  3. 서비스 분리
    • 재고 관리, 결제, 알림 등 서비스 간 독립적이고 느슨한 결합.
  4. 오류 처리 및 복구
    • 실패 시 재처리(재시도)데드 레터 큐(Dead Letter Queue) 사용.
  5. 실시간 피드백
    • 사용자에게 즉각적인 상태 알림 제공.

🛠️ 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 재시도 설정으로 복구 시도.
@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);
    }
}

🛡️ 실패 처리 전략

  1. Kafka 재시도
    • Spring Kafka의 RetryTemplate을 사용하여 메시지 재처리.
    • 설정 예시:
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
  1. 데드 레터 큐(Dead Letter Queue)
    • Listener처리 실패 메시지를 별도의 Kafka 토픽으로 이동.
    • 실패한 데이터를 분석 및 복구하는 데 사용.
    • Spring Kafka에서 데드 레터 큐를 활성화하려면 error-handling 설정을 추가합니다.
  1. 트랜잭션 관리
    • Kafka 트랜잭션 API를 사용하여 데이터베이스 작업과 Kafka 이벤트 발행을 원자적으로 처리.
  2. 모니터링 및 로깅
    • Kafka와 Spring Boot의 Actuator를 활용한 실시간 모니터링.
    • 예외 발생 시 로깅 및 알림을 통해 장애 대응.
  3. Outbox 패턴
    • Outbox 패턴은 Kafka와 RDBMS를 함께 사용하는 환경에서 데이터 일관성과 신뢰성을 보장
    • Kafka 발행 실패와 같은 상황에서 메시지 유실을 방지하며 안정적인 시스템을 설계

오류 처리 핵심: 재시도, 데드 레터 큐, 트랜잭션 관리, 모니터링을 통해 시스템 신뢰성을 극대화합니다.


🎉 정리

  • Kafka의 동작 방식:
    KafkaProducer, Broker, Consumer, Topic, Partition 등으로 구성된 분산 메시징 시스템으로, 데이터를 생성, 저장, 소비하는 과정에서 높은 확장성과 안정성을 제공합니다.
  • Kafka의 장단점:
    Kafka는 대규모 데이터 스트리밍과 내구성이 강점이지만, 설정 및 운영이 복잡하며 실시간 처리가 필요한 경우 적합하지 않을 수 있습니다.
  • RabbitMQ와의 차이점:
    RabbitMQ는 단순하고 빠른 메시징에 적합하며, Kafka는 대규모 데이터 처리 및 스트리밍에 특화된 플랫폼입니다.
반응형