Kafka 정리 (1)

Hyunni·2024년 11월 6일

Kafka는 대규모 데이터를 빠르게 전송하고 처리할 수 있도록 설계된 분산 메시징 시스템으로, 고가용성, 확장성, 내구성을 제공합니다. 이번 블로그에서는 Kafka의 기본 개념부터 Docker Compose로 다중 Broker를 구성하는 방법, 파티셔닝과 Consumer Group을 통한 병렬 처리, 메시지 중복 방지 및 내구성을 보장하는 방식까지 자세히 설명해 보겠습니다.

1. Kafka의 기본 개념

Kafka의 핵심 구성 요소를 먼저 정의해 보겠습니다.

  • Producer: 데이터를 생성하여 Kafka의 특정 Topic으로 전송하는 애플리케이션입니다.
  • Consumer: 특정 Topic의 데이터를 구독하여 사용하는 애플리케이션입니다.
  • Broker: Kafka에서 메시지를 저장하고 관리하는 기본 단위 서버입니다. 여러 Broker가 모여 하나의 Kafka 클러스터를 구성하며, 이를 통해 Kafka는 데이터의 고가용성을 유지하고 확장성을 제공합니다.
  • Topic: Producer가 전송한 메시지를 저장하는 단위입니다. Topic은 Partition이라는 단위로 분할되어, 메시지를 효율적으로 관리하고 병렬 처리를 가능하게 합니다.

Broker와 클러스터 구성 방식

Kafka 클러스터는 여러 Broker가 모여 구성됩니다. 각 Broker는 독립적인 서버로, 메시지 일부를 저장하고 관리하며, Kafka는 Zookeeper를 통해 클러스터 내 Broker의 상태를 공유하고 조율합니다. 클러스터 구성 방식 덕분에 Kafka는 고가용성과 확장성을 제공할 수 있습니다. 예를 들어, 클러스터에 Broker를 추가하면 데이터 처리량을 쉽게 늘릴 수 있습니다.

Docker Compose를 사용한 다중 Broker 구성 예제

Docker Compose를 통해 Kafka 클러스터를 로컬 환경에서 실험할 수 있습니다. 다음은 Zookeeper와 두 개의 Kafka Broker로 구성된 클러스터 설정 예제입니다.

version: '3'
services:
  zookeeper:
    image: wurstmeister/zookeeper:3.4.6
    ports:
      - "2181:2181"

  kafka1:
    image: wurstmeister/kafka:2.12-2.3.0
    ports:
      - "9092:9092"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2

  kafka2:
    image: wurstmeister/kafka:2.12-2.3.0
    ports:
      - "9093:9093"
    environment:
      KAFKA_BROKER_ID: 2
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9093
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2

docker-compose up -d 명령어를 통해 실행하면 두 개의 Broker가 있는 Kafka 클러스터를 사용할 수 있으며, 각각의 Broker는 Topic의 데이터를 독립적으로 저장하고 복제하여 고가용성을 보장합니다.


2. Partitioning과 Consumer Group을 통한 병렬 처리

Kafka의 Topic은 파티션(Partition)이라는 단위로 나누어져 저장됩니다. 파티션을 통해 Kafka는 높은 처리 성능과 확장성을 제공합니다.

Partitioning의 장점

  • 병렬 처리: Topic을 여러 파티션으로 나누면 여러 Consumer가 각 파티션의 데이터를 병렬로 처리할 수 있습니다.
  • 확장성: 각 파티션을 다른 Broker에 나누어 저장함으로써 데이터를 분산하고 확장성을 높일 수 있습니다.

Consumer Group과 파티션의 관계

Kafka의 Consumer Group은 여러 Consumer가 같은 Topic을 병렬로 처리할 수 있게 해주는 그룹입니다. 같은 Consumer Group에 속한 Consumer는 각기 다른 파티션을 할당받아 메시지를 처리합니다.

예를 들어 concert-topic을 10개의 파티션으로 나누고, ServerB에 10개의 컨테이너(Consumer)가 구동 중이라면 아래와 같은 방식으로 각 Consumer가 파티션을 나누어 처리할 수 있습니다.

concert-topic (10 partitions) → ServerB-1 Consumer (partition 1)
                             → ServerB-2 Consumer (partition 2)
                             → ServerB-3 Consumer (partition 3)
                             ...
                             → ServerB-10 Consumer (partition 10)

이렇게 각 파티션을 Consumer에 분산하여 처리함으로써, ServerB의 컨테이너들이 병렬로 작업을 분담하게 됩니다. 만약 Consumer 수보다 파티션이 적다면 일부 Consumer는 대기 상태가 될 수 있습니다.

메시지 중복 방지 방식

Kafka는 같은 Consumer Group 내에서 메시지 중복을 방지합니다. 각 파티션은 동일한 Consumer Group에 속한 단 하나의 Consumer에만 할당됩니다. 예를 들어, user auser b가 동시에 concert-topic으로 메시지를 전송해도, 이 메시지는 각기 다른 파티션에 저장되고, 해당 파티션을 할당받은 Consumer만이 메시지를 읽습니다. 따라서 동일한 메시지가 여러 Consumer에 중복으로 전달되지 않도록 보장됩니다.


3. 순차적 메시지 전송이 필요한 경우

Kafka의 파티셔닝은 병렬 처리를 위해 설계되었으므로, 순차적인 메시지 처리가 필요한 경우에는 파티션 구성을 주의해야 합니다.

  • 파티션이 여러 개인 경우: 여러 파티션이 병렬로 처리되므로, 각 파티션 간 메시지 순서가 보장되지 않습니다.
  • 단일 파티션을 사용하는 경우: 모든 메시지가 하나의 파티션에 쌓이므로, 순차적인 처리가 가능합니다.

만약 ServerA에서 시작한 메시지가 ServerB를 거쳐 ServerC로 순차적으로 처리되어야 한다면, 각 단계마다 단일 파티션을 사용하여 순서를 유지하는 방식으로 설계하는 것이 좋습니다.

ServerA → (produce to) Topic1 (1 partition) → (consume from) ServerB
ServerB → (produce to) Topic2 (1 partition) → (consume from) ServerC

4. Kafka의 내구성 보장 - Replication과 Acknowledgment

Kafka는 데이터의 내구성을 보장하기 위해 ReplicationAcknowledgment 기능을 제공합니다.

  • Replication: 각 파티션 데이터를 여러 Broker에 복제하여, 특정 Broker에 장애가 발생해도 데이터가 손실되지 않도록 합니다. 예를 들어 replication.factor를 2로 설정하면 각 파티션이 두 개의 Broker에 저장됩니다.
  • Acknowledgment (acks): Producer가 메시지를 전송한 후, 성공 여부를 확인하는 방식입니다.
    • acks=0: Producer가 메시지 전송 후 성공 여부를 확인하지 않습니다.
    • acks=1: 리더 Broker가 메시지를 수신하면 성공으로 간주합니다.
    • acks=all: 모든 복제된 Broker가 메시지를 수신한 후에만 성공으로 간주합니다.

예제: 내구성을 보장하는 Producer 코드

아래는 acks=allreplication.factor=2로 설정한 예제 코드입니다.

Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("acks", "all");  // 모든 복제된 Broker에게 수신 확인
props.put("retries", 3);  // 실패 시 재시도
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

KafkaProducer<String, String> producer = new KafkaProducer<>(props);
producer.send(new ProducerRecord<>("concert-topic", "key", "Durable Concert Ticket Request"));
producer.close();

이렇게 설정하면 메시지가 모든 복제 노드에 저장된 후에만 성공으로 간주되므로 데이터의 내구성이 보장됩니다.


결론

Kafka는 고성능 분산 메시징 시스템으로, 병렬 처리와 내구성, 고가용성을 모두 갖춘 데이터 처리 플랫폼입니다. 클러스터 구성과 파티셔닝을 통해 데이터 분산과 병렬 처리 성능을 높이고, Consumer Group을 통해 메시지 중복을 방지하면서 고가용성 보장과 확장성을 제공합니다.

다음 블로그에선 Kafka Streams API, 트랜잭션, Schema Registry 등을 다루며 Kafka의 고급 기능을 추가로 살펴보겠습니다.

profile
9년차 Fullstack Developer로 고민하고 구현하는 것들에 대해 정리하는 공간입니다.

0개의 댓글