개발 이야기/Kafka

[Kafka] (2) Topic과 Partition

올리버 2022. 3. 23. 20:43

Events, Streams, Topics

Events

Event란, 과거에 일어난 사실을 뜻한다.

Event는 발생함으로 인해 변화된 상태를 가지고 시스템 사이를 오가는 불변의 데이터이다.

 

Streams

Event Stream이란, 관련된 Event들을 나타낸다.

 

Topics

Event Stream이 Kafka에서는 Topic이란 이름으로 저장된다.

Kafka에서는 Topic이 구체화된 Event Stream을 뜻한다.

Topic은 데이터베이스의 테이블이나 파일 시스템의 폴더들 같이 연관된 Event를 묶어 영속화한다.

 

Topic은 Kafka에서 Producer 와 Consumer 를 분리하는 중요한 컨셉이다.

Producer 는 Topic에 Message(Event)를 저장 (Push) 하고 Consumer 는 저장된 Message(Event)를 읽어 (Pull) 온다.

하나의 Topic에 여러 Producer, Consumer 가 존재할 수 있다.

 

아래 그림을 보면 연관된 Event가 모여 Stream 을 이루게 되고, Topic 의 이름으로 Kafka에 저장된다.


Partitions

Topic은 여러 Partition으로 나눠진다.

Topic이 일종의 논리적인 개념이라면, Partition은 Topic에 속한 레코드를 실제 저장소에 저장하는 가장 작은 단위이다. 각각의 Partition은 Append-Only 방식으로 기록되는 하나의 로그 파일이다.

아래 그림에서 레코드는 Partition에 저장되는 Message를 의미한다.

Offset과 Messages의 순서

Partition의 레코드는 각각 Offset을 가진다.

Offset은 Partition내에서 고유한 레코드의 순서를 의미하는 식별자 정보를 가진다.

Offset 정보는 Kafka에 의해서 관리되고, 값이 계속 증가하며 불변하는 숫자 정보이다.

레코드가 Partition에 쓰여질 때 항상 기록의 맨 뒤에 쓰여지고(Append Only), 다음 순서의 Offset 값을 갖게 된다.

Offset은 Partition에서 레코드를 읽고 사용하는 Consumer 에게 특히 유용하다.

 

아래의 그림은 세 개의 Partition을 갖는 Topic을 보여준다.

레코드는 각 Partition의 마지막에 추가된다.

Message는 Partition 내에서는 유의미한 순서를 가지고 이를 보장하지만, Partition 간에는 보장되지 않는다.

그래서 Offset이 존재함에도 Message의 순서가 보장되지 않는 것이다.

 

Partition으로 인한 Kafka의 확장성

Kafka Cluster는 Broker라 불리는 하나 혹은 그 이상의 서버들로 이루어지는데, 각각의 Broker는 전체 클러스터에 속한 레코드의 서브셋을 가진다.

Kafka는 Topic의 Partition들을 여러 Broker에 배포하는데 이로 인해 얻을 수 있는 다음과 같은 이점들이 있다.

  • 한 Topic의 모든 Partition을 하나의 Broker에 넣을 경우,
    해당 Topic의 확장성은 Broker의 I/O 처리량에 의해 제약되지만, Partition들을 여러 Broker에 나누면,
    하나의 Topic은 수평적으로 확장될 수 있고 이로 인해 Broker의 처리 능력보다 더 큰 성능을 제공할 수 있게 된다.
  • Topic은 여러 Consumer 에 의해 동시에 처리될 수 있다.
    단일 Broker에서 모든 Partition을 제공하면 지원할 수 있는 Consumer 수가 제약되는데,
    여러 Broker에서 Partition을 나누어 제공함으로써 더 많은 Consumer 들이 동시에 Message를 처리할 수 있게 된다.
  • 동일한 Consumer 의 여러 인스턴스가 서로 다른 Broker에 있는 Partition에 접속함으로써 매우 높은 메시지 처리량을 가능하게 한다. 각 Consumer 인스턴스는 하나의 Partition에서 Message를 제공받고,
    각각의 레코드는 명확한 처리 담당자가 존재함을 보장할 수 있다.

Partition 복제를 통해 장해 극복

  • Kafka는 여러 Broker에 동일한 Partition의 복사본, 즉 Replica를 2개 이상 유지한다.
    Broker에 장애가 발생해도 Replica를 통해 Consumer에게 지속적으로 Message를 제공할 수 있다.

Partition에 레코드 작성하는 방법(Producer)

1. Partition Key 사용

Producer 는 Partition Key를 사용해서 특정한 Partition에 Message를 전달할 수 있다.

Partition Key는 해싱 함수를 통해 전달되고 해싱 함수는 동일한 Key를 갖는 모든 레코드들이 동일한 Partition에 도착하는 것을 보장한다.

Partition Key를 지정할 경우, 관련된 Event를 동일한 Partition에 유지함으로써 전달된 순서가 그대로 유지되는 것을 보장할 수 있다. 다만 Key가 제대로 분산되지 않는 경우 특정 Broker만 Message를 받는 단점이 있다.

 

 

2. Kafka에 의한 분배

Producer 가 Partition Key를 명시하지 않으면, Kafka 는 Round-Robin 방식을 사용해 Partition을 배정한다.

레코드의 순서는 보장되지 않지만, 해당 Topic의 Partition들에 고루 분배된다.

 

3. 커스텀 Partitioner 구현

Producer 가 서로 다른 비즈니스 규칙을 사용한 자체 Partitioner 구현을 사용할 수도 있다.


Partition으로 부터 레코드 읽기(Consumer)

Kafka 는 Message를 Consumer 에 전달(Push) 하지 않고, Consumer 가 Partition으로부터 Message를 읽어(Pull) 가야 한다. Consumer는 Broker의 Partition과 연결해, Message들이 쓰여진 순서대로 메시지를 읽는다.

 

Message의 Offset 은 커서와 같이 동작한다.

Consumer 는 Message의 Offset으로 커서를 이동하여 Message를 읽는다.(Offset 이동 -> Message 읽기 반복)

 

각 Partition에서 마지막으로 소비된 Message의 Offset 을 기억함으로써 Consumer는 장애가 나도 복구 후에 Message 소비를 재시작할 수 있다.

 

Partition은 하나 이상의 Consumer 들로부터 소비될 수 있고, 각각은 서로 다른 Offset 을 가지고 Message를 읽을 수 있다.

Kafka는 Consumer Group 이라는 개념을 사용한다.(어떤 Topic을 소비하는 Consumer 들을 그룹으로 묶은 것)

동일한 Consumer Group 에 있는 Consumer 들은 동일한 Group-Id 를 부여받는다.

Partition 내의 Message는 Group 내의 Consumer 중 하나에 의해서만 소비되는 것을 보장한다.

Consumer Group 은 Consumer 들이 병렬적으로 Message를 소비할 수 있도록 한다.

병렬 처리를 가능케 함으로써 처리량은 매우 높아지지만, Group의 Maximum Parallelism 은 Topic의 Partition 개수와 동일하다.

 

여기서 중요한 것은, Consumer 의 개수가 토픽의 병렬성 정도를 결정하지 않고, 결정하는 것은 파티션의 개수라는 것이다. 하지만 파티션을 늘리는 것이 항상 장점으로 작용하지는 않는다. 이와 관련해선 차후에 정리하도록하자.

 

 참고 

 

https://kafka.apache.org/documentation/

https://medium.com/event-driven-utopia/understanding-kafka-topic-partitions-ae40f80552e8

https://devidea.tistory.com/95