일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Data Engineer
- 코딩테스트
- Apache Kafka
- Data Engineering
- Iceberg
- apache iceberg
- 영어
- 백준
- Kafka
- 코엑스
- java
- 알고리즘
- BigData
- Spark
- 개발
- 여행
- hadoop
- bigdata engineering
- 코엑스맛집
- 코딩
- Trino
- 프로그래머스
- 용인맛집
- HIVE
- 맛집
- 코테
- 자바
- pyspark
- bigdata engineer
- 삼성역맛집
- Today
- Total
지구정복
[Kafka] 7. Partition 본문
참고하면 좋은 블로그글
https://colevelup.tistory.com/18
1. Kafka partition
Apache Kafka는 고성능 데이터 파이프라인과 스트리밍 분석을 위한 오픈 소스 분산 이벤트 스트리밍 플랫폼입니다.
Kafka의 확장성(scalability)과 성능(performance)을 가능하게 하는 핵심 요소 중 하나는 토픽(topic)과 파티션(partition)이라는 개념입니다.
Kafka에서 메시지는 “토픽”에 기록됩니다.
토픽은 이벤트의 논리적인 그룹으로, 생산자(producer)와 소비자(consumer)가 메시지를 분류하는 데 도움을 줍니다.
이러한 토픽은 Kafka 브로커에 저장됩니다.
Kafka는 토픽을 더 작은 단위인 파티션(partition)으로 나눕니다.
파티션은 추가 전용(append-only)이고 순서가 보장된 로그 파일로, 토픽 데이터의 일부를 저장합니다.
하나의 토픽은 여러 개의 파티션을 가질 수 있으며, 이를 통해 메시지 처리에서 병렬성(parallelism)을 구현할 수 있습니다.
이 글에서는 파티션이 무엇이며 왜 중요한지 설명하고,
생산자와 소비자를 위한 주요 파티션 전략들에 대해서도 다룹니다.
2. Summary of Kafka partition concepts
Kafka는 프로듀서가 메시지를 파티션에 어떻게 게시할지, 그리고 컨슈머가 파티션에 어떻게 할당될지 선택할 수 있도록 합니다.
이 작업을 수행하는 방법에는 여러 가지가 있으며, 각각의 방식은 장단점을 가지고 있습니다.
아래 표는 중요한 파티셔닝 전략들을 요약한 것입니다.
✅ 프로듀서 파티션 전략 (Producer Partition Strategies)
기본(Default) | **키 해시(key hash)**를 사용하여 메시지를 파티션에 매핑합니다. 키는 해시 함수에 전달되어 해시 값을 얻고, 그 값을 기반으로 메시지가 속할 파티션을 결정합니다. |
라운드 로빈(Round-robin) | 메시지가 파티션에 라운드 로빈 방식으로 매핑됩니다. 키가 null로 설정된 메시지는 순차적으로 각 파티션에 분배됩니다. |
스티키(Sticky) | 프로듀서 요청이 특정 파티션을 지정하면 해당 레코드는 항상 그 파티션으로 전송됩니다. 메시지는 배치 크기와 시간 제약 내에서 전송되어 **지연 시간(latency)**을 줄입니다. |
사용자 정의(Custom) | Kafka의 파티션 방식을 사용자 정의 로직으로 재정의하는 인터페이스를 구현합니다. 이를 통해 키를 파티션에 매핑하는 맞춤형 전략을 적용할 수 있습니다. |
✅ 컨슈머 파티션 할당 전략 (Consumer Assignment Strategies)
범위 기반(Range, 기본값) | 같은 파티션 번호는 같은 컨슈머에게 할당됩니다. (예: 같은 컨슈머가 Topic X의 P0과 Topic Y의 P0을 읽음) |
라운드 로빈(Round-robin) | 각 파티션이 개별적으로 컨슈머에게 순차 할당됩니다. (예: 첫 번째부터 마지막까지 순서대로 할당) |
스티키(Sticky) | 라운드 로빈 할당 방식과 유사하지만, 기존 할당을 최대한 유지하려고 시도합니다. 즉, 파티션 재분배 시에도 기존 매핑을 가능한 한 그대로 유지합니다. |
사용자 정의(Custom) | AbstractPartitionAssignor 클래스를 확장하여, assign() 메서드를 사용자 정의 로직으로 재정의할 수 있습니다. 이를 통해 고유한 파티션 할당 전략을 구현할 수 있습니다. |
3. Partition basics
전략들을 살펴보기 전에, 먼저 **파티셔닝(partitioning)**에 대한 배경 정보를 간단히 알아보겠습니다.
잘 알고 있듯이, **프로듀서(producer)**는 Kafka 토픽에 메시지를 작성하고, **컨슈머(consumer)**는 자신이 관심 있는 토픽에서 메시지를 읽습니다.
하지만 다수의 프로듀서와 컨슈머가 존재하는 시스템에서는, 하나의 서버(브로커)가 모든 토픽 메시지를 저장하게 되면 **병목 현상(bottleneck)**이 발생할 수 있습니다.
바로 이때, **토픽 파티션(topic partitions)**이 중요한 역할을 합니다.
각 파티션을 서로 다른 브로커에 호스팅할 수 있기 때문에, 하나의 토픽을 여러 브로커에 수평적으로 분산시켜, 단일 서버의 성능 한계를 넘어서는 확장성을 제공합니다.
또한 동일한 토픽을 여러 컨슈머(동일 컨슈머 그룹 내)가 동시에 읽을 수 있게 됩니다.
추가로, **파티션을 복제(replication)**할 수도 있어, 하나의 서버가 장애를 일으켜도 다른 브로커가 동일한 파티션의 복사본을 보유하고 있어 데이터 손실을 방지할 수 있습니다.
이처럼 Kafka는 **파티션을 통해 중복성(redundancy)**과 **확장성(scalability)**을 동시에 제공합니다.
3.1. Choosing the right number of partitions
Kafka를 설정할 때, 각 토픽에 대해 파티션 개수를 직접 지정하게 됩니다.
이것은 매우 중요한 결정이며, 정답은 하나가 아닙니다.
토픽의 파티션 수를 정할 때 고려해야 할 여러 가지 요소들이 있습니다:
✅ 파티션 수 결정 시 고려 요소
- 메시지 양 (Message volume):
파티션 수가 많을수록 **처리량(throughput)**이 증가하지만, 많은 수의 파티션을 관리하는 일은 복잡해집니다. - 병렬성 (Parallelism):
컨슈머 그룹 내 컨슈머 수와 파티션 수가 같을 때 부하 분산이 가장 잘 이루어집니다.
파티션 수가 적으면, 일부 컨슈머는 할당을 받지 못하고 대기 상태가 됩니다. - 브로커 제약 (Broker limitations):
브로커의 **사양(RAM과 CPU)**은 한 브로커가 감당할 수 있는 파티션 수를 제한합니다.
저사양 브로커에 너무 많은 파티션이 할당되면, 성능 저하가 발생할 수 있습니다. - 스토리지 (Storage):
Kafka에서 각 파티션은 디스크의 **세그먼트(segment)**로 저장됩니다.
너무 많은 파티션을 생성하면 저장소 오버헤드가 증가할 수 있습니다.
⚠️ 참고로, 운영 중인 토픽의 파티션 수는 줄일 수 없습니다.
따라서 이상적으로는 적은 수로 시작해서 점진적으로 늘리는 것이 좋습니다.
Kafka 권장 기준에 따르면:
- 브로커당 4,000개 이하의 파티션,
- 클러스터 전체 기준으로는 200,000개 이하의 파티션을 유지하는 것이 좋습니다.
(※ 클러스터 = 함께 작동하는 브로커 집합)
4. Kafka partition strategies for producers
파티셔닝 전략을 이해하려면, 먼저 Kafka 메시지의 구조를 이해해야 합니다.
개념적으로, Kafka 메시지는 키(key), 값(value), 타임스탬프(timestamp), 그리고 **선택적인 메타데이터 헤더(headers)**로 구성됩니다.
다음은 Kafka 메시지의 예시입니다:
- Key: "Temperature sensor" (온도 센서)
- Value: "Recorded a temperature of 25℃" (25℃를 기록함)
- Timestamp: "2024년 4월 26일 오후 1시"
Kafka는 하나의 파티션 내에서는 메시지가 작성된 순서대로 정확히 저장되고 전달되는 것을 보장합니다.
그리고 프로듀서의 파티셔닝 전략은 해당 메시지를 어떤 파티션에 기록할지 결정하는 역할을 합니다.
4.1. Default partitioner
**키가 null(또는 비어 있음)**일 경우, Kafka는 해당 레코드를 사용 가능한 토픽 파티션 중 하나에 무작위로 전송합니다.
반면에 키가 존재하면, Kafka는 해당 키의 해시(hash) 값을 기반으로 파티션 번호를 계산하여 메시지를 매핑합니다.
이 방식 덕분에, 같은 키를 가진 메시지들은 항상 같은 파티션에 저장되게 됩니다.
하지만, 이 **매핑은 토픽의 파티션 수가 변하지 않을 때만 일관성(consistency)**을 유지합니다.
새로운 파티션을 추가하면, Kafka는 이전과는 다른 파티션에 같은 키의 메시지를 기록할 수도 있습니다.
4.2. Round robin partitioner
이 방법은 프로듀서가 모든 파티션에 균등하게 메시지를 분산시키도록 하고 싶을 때 사용할 수 있습니다.
프로듀서는 연속된 메시지들을 순차적으로 각기 다른 파티션에 전송하며,
모든 파티션을 한 번씩 사용하고 나면 처음부터 다시 반복합니다.
이 경우, 같은 키를 가진 메시지들도 서로 다른 파티션에 분산될 수 있습니다.
라운드 로빈(round-robin) 전략은 같은 키에 대해 많은 메시지가 생성되는 경우에 특히 유용합니다.
기본 전략(default strategy)을 사용할 경우, 해당 키에 매핑된 파티션만 과부하되어 핫 파티션(hot partition) 문제가 발생할 수 있습니다.
반면에, 라운드 로빈 전략은 모든 파티션에 고르게 메시지를 분배하므로 부하 분산 효과를 얻을 수 있습니다.
4.3. Uniform sticky partitioner
파티션과 키가 지정되지 않은 경우, 프로듀서는 기본적으로 라운드 로빈(round-robin) 전략을 사용합니다.
이 전략은 레코드를 모든 파티션에 고르게 분산시키지만, 그 결과 **배치(batch)**가 더 작아지는 문제가 생깁니다.
(예: 파티션이 3개라면, 각 파티션에 하나씩만 들어가 배치 크기가 3이 되어, 무한한 데이터 스트림에서는 너무 작음)
이로 인해 큐잉 요청 증가와 지연 시간(latency) 상승으로 성능 저하가 발생할 수 있습니다.
이 문제를 해결하기 위해 Kafka는 **Uniform Sticky Partitioner(균일 스티키 파티셔너)**를 도입했습니다.
이 전략은 두 가지 규칙을 따릅니다:
- 파티션 정보가 포함된 레코드는 지정된 대로 사용됩니다.
- 파티션 정보가 없는 레코드는, 배치가 가득 차거나 전송 대기 시간이 끝날 때까지 같은 파티션에 저장됩니다.
즉, 스티키 파티션(sticky partition)은 배치 단위로만 변경되며, 이를 통해 더 큰 배치를 생성하고, 시스템 지연을 줄일 수 있습니다.
시간이 지남에 따라, 레코드는 모든 파티션에 균등하게 분산됩니다.
하지만 주의할 점은, 같은 키를 가진 레코드라고 해도 반드시 같은 파티션에 전송되는 것은 아니라는 점입니다.
정리하면 프로듀서가 메시지를 보낼 때 record batch(한 번에 보내는 메시지의 양, batch.size 설정값으로 설정)가 쌓이면 한 번에 특정 파티션으로 보내는 것을 의미한다.
4.4. Custom partitioner
때로는 특정 사용 사례가 Kafka의 기본 제공 파티션 전략과 맞지 않을 수 있습니다.
예를 들어, Kafka를 사용해 사용자 피드 업데이트와 활동 로그를 처리하는 소셜 미디어 플랫폼을 생각해 봅시다.
이 플랫폼에 “PU”라는 **파워 유저(Power User)**가 있고, 이 사용자는 전체 이벤트의 **30~40%**를 차지한다고 가정해 보겠습니다.
만약 기본 해시 파티셔닝(default hash partitioning)을 사용한다면, Kafka는 “PU” 사용자의 레코드를 다른 사용자들과 같은 파티션에 배정하게 됩니다.
이 경우, 하나의 파티션에 데이터가 집중되어 병목 현상이 발생하게 됩니다.
이러한 상황의 이상적인 해결책은 “PU” 사용자에게 전용 파티션을 할당하고,
나머지 사용자들은 기존 해시 파티셔닝 방식으로 분산시키는 것입니다.
이 전략은 Java 코드에서 kafka.clients.producer.Partitioner 클래스를 **구현(implement)**하여 적용할 수 있습니다.
해당 전략을 구현하는 전체 코드는 Kafka 파티셔닝 전략 가이드에서 확인하실 수 있습니다.
5. Consumers and Kafka partitions
**Kafka에서 데이터를 읽는 주체는 컨슈머(Consumer)**입니다.
만약 여러 프로듀서가 하나의 토픽에 메시지를 지속적으로 쓴다면,
단일 컨슈머가 모든 메시지를 처리하지 못하고 점점 밀리게 될 수 있습니다.
이러한 소비 확장을 위해 Kafka는 **컨슈머 그룹(consumer group)**이라는 개념을 제공합니다.
이름 그대로, Kafka 컨슈머 그룹은 동일한 토픽을 구독하는 여러 컨슈머들로 구성된 집합입니다.
토픽 내 파티션들이 그룹의 컨슈머들에게 나뉘어 할당되며, 각 컨슈머는 서로 다른 파티션 세트에서 메시지를 수신합니다.즉, 컨슈머 그룹 내부에서 데이터 처리를 분산하는 구조입니다.
Kafka의 규칙 중 하나는 각 파티션은 오직 하나의 컨슈머에게만 할당된다는 점입니다.
따라서 **컨슈머 수가 파티션 수보다 많으면, 일부 컨슈머는 유휴 상태(idle)**가 됩니다.
유휴 상태의 컨슈머는 기존 컨슈머가 실패했을 때만 작업을 대신 수행합니다.
Kafka는 토픽의 파티션에 데이터를 쓰는 방식을 제어할 수 있는 것처럼,
컨슈머가 파티션에서 데이터를 읽는 방식도 설정할 수 있도록 지원합니다.
이를 위해 Kafka는 partition.assignment.strategy 프로퍼티를 통해
컨슈머 파티션 전략을 구성할 수 있도록 합니다.
5.1. Consumer rebalancing
Kafka에서는 실행 중인 상태에서도 컨슈머 그룹에 컨슈머를 추가하거나 제거할 수 있습니다.
새로운 컨슈머가 그룹에 추가되면, 설정된 컨슈머 파티션 전략에 따라 파티션으로부터 메시지를 소비하기 시작합니다.
반대로, 컨슈머를 제거하거나 해당 컨슈머에 장애가 발생하면, Kafka는 해당 컨슈머가 담당하던 파티션을 나머지 활성 컨슈머에게 재할당합니다.
이러한 컨슈머 간의 파티션 소유권 재할당 과정을 **리밸런싱(rebalancing)**이라고 합니다.
리밸런싱에는 두 가지 유형이 존재합니다.
✅ Eager Rebalancing (이거 리밸런싱)
컨슈머 그룹 구성원이 변경될 때마다,
그룹 내 모든 컨슈머가 잠시 데이터 읽기를 중단하고,
**기존 파티션 소유권을 포기한 뒤 다시 그룹에 재참여(rejoin)**합니다.
그 후 Kafka는 모든 컨슈머에게 새롭게 파티션을 재할당합니다.
이 과정에서 모든 컨슈머가 일시적으로 데이터를 읽지 못하게 되어,
**짧은 시간 동안의 서비스 중단(unavailability)**이 발생합니다.
✅ Cooperative Rebalancing (협조적 리밸런싱)
리밸런싱을 여러 단계로 나누어 점진적으로 수행합니다.
한 번에 모든 파티션을 재할당하는 것이 아니라,
일부 파티션만 선택적으로 재할당하여 변경합니다.
이렇게 점진적으로 변화가 이루어지기 때문에,
항상 일부 컨슈머는 메시지를 계속 처리하고 있으며,
전체 중단(total unavailability) 상황을 피할 수 있습니다.
5.2. Static group membership avoids rebalancing
리밸런싱은 일반적인 상황에서는 바람직하지 않은 경우도 많습니다,
왜냐하면 컨슈머의 일시적인 중단(consumer downtime) 문제가 발생하기 때문입니다.
**실시간 이벤트 처리(use case)**에서는 몇 밀리초의 지연조차 허용되지 않는 경우가 많기 때문에,
**리밸런싱을 회피하기 위한 방법으로 정적 멤버십(static membership)**을 사용할 수 있습니다.
일반적인 리밸런싱은, **컨슈머 그룹 내에서 컨슈머의 정체성이 일시적(transient)**이라는 전제 하에 작동합니다.
즉, 어떤 컨슈머가 그룹을 떠났다가 다시 참여하면, 기존에 맡고 있던 파티션과는 무관하게 새로운 파티션을 할당받게 됩니다.
그러나 컨슈머에 고유한 group.instance.id 속성을 설정하면, 이 기본 동작을 **무시(override)**할 수 있습니다.
이 속성은 해당 컨슈머에게 **정적 멤버십(static membership)**을 부여합니다.
정적 멤버가 그룹에 처음 참여할 때는,
설정된 파티션 할당 전략에 따라 파티션이 지정됩니다.
그리고 해당 컨슈머가 종료되었다가 다시 그룹에 참여해도,
Kafka는 컨슈머의 고유 ID를 인식하고
이전에 소비하던 동일한 파티션을 다시 할당합니다.
이때는 리밸런싱이 발생하지 않습니다.
6. Kafka partition strategies for consumers
🔹 Range Assignor (범위 할당자)
이 전략은 컨슈머 그룹 내 컨슈머들에게 연속적인 파티션을 할당합니다.
Range 할당자는 전체 파티션 수를 컨슈머 수로 나누어, 각 컨슈머가 맡아야 할 파티션 수를 계산합니다.
나누어떨어지지 않는 경우에는, 앞쪽 몇몇 컨슈머가 파티션을 하나씩 더 받게 됩니다.
이 전략은 간단하고 효율적하지만, 항상 균등한 워크로드 분배로 이어지지는 않습니다.
🔹 Round Robin Assignor (라운드 로빈 할당자)
프로듀서에서와 마찬가지로, 컨슈머에서도 파티션을 더 균등하게 분배하는 것을 목표로 합니다.
파티션은 라운드 로빈 방식으로 순차적으로 컨슈머에게 할당됩니다.
예: 첫 번째 파티션은 첫 번째 컨슈머에게, 두 번째는 다음 컨슈머에게, 마지막 멤버까지 할당한 후 다시 처음으로 순환합니다.
이 전략은 토픽마다 파티션 수가 다르거나, 파티션마다 데이터 양이 다른 경우에 유용합니다.
다만, 리밸런싱 시 파티션 이동을 줄이지는 못합니다.
🔹 Sticky Assignor (스티키 할당자)
Sticky 할당자는 리밸런싱 시 파티션 할당의 안정성을 높입니다.
즉, 리밸런싱이 발생해도 가능한 한 기존의 파티션 할당을 유지하려고 합니다.
만약 컨슈머가 새로운 파티션 세트를 할당받아야 한다면, 변화를 최소화해 리밸런싱에 필요한 오버헤드를 줄입니다.
이는 컨슈머가 파티션 초기화 비용이 큰 시스템에서 특히 효과적이며,
**더 안정적인 소비(consumption stability)**를 보장합니다.
🔹 Custom Assignor (사용자 정의 할당자)
Kafka에서는 AbstractPartitionAssignor 클래스를 **확장(extend)**하여
사용자 정의 파티션 할당 전략을 구현할 수 있습니다.
예를 들어 다음과 같은 요소에 따라 파티션을 분배할 수 있습니다:
- 각 컨슈머의 처리 능력(capacity)
- 과거 데이터 소비 패턴(historical consumption)
- 워크로드의 특성(workload characteristics)
전체 구현 코드는 Kafka 파티셔닝 전략 가이드에서 확인할 수 있습니다.
7. Common pitfalls with Kafka partitions
Kafka 토픽에 적절한 파티션 전략을 선택하는 것은, 효율적이고 확장 가능한 데이터 처리를 보장하기 위해 매우 중요합니다.
다음은 흔히 발생할 수 있는 주의해야 할 실수들입니다:
⚠️ 파티션 과다 설정 (Overpartitioning)
파티션 수를 많이 생성한다고 해서 항상 성능이 향상되는 것은 아닙니다.
Kafka는 컨슈머 그룹 구성원이 변경될 때마다 파티션을 브로커 간에 리밸런싱합니다.
파티션 수가 너무 많으면 리밸런싱에 시간이 오래 걸려, 시스템 가용성 및 성능 저하를 초래할 수 있습니다.
또한, 메시지 양에 비해 파티션 수가 과도하게 많으면, 일부 파티션은 거의 비어 있게 되어,
브로커 자원이 비효율적으로 사용될 수 있습니다.
⚠️ 파티션 과소 설정 (Underpartitioning)
반대의 극단도 문제입니다. 파티션 수가 너무 적으면, 각 파티션에 너무 많은 메시지가 몰려
백로그(backlog), 처리 지연, 처리량 저하로 이어질 수 있습니다.
특히, 해당 토픽을 호스팅하는 브로커는 메시지 처리량을 따라가지 못해
CPU 과부하, 디스크 I/O 병목 등의 문제가 발생할 수 있습니다.
**파티션 과소 설정은 종종 ‘반응적 스케일링(reactive scaling)’**을 유발합니다.
즉, 초기에 향후 메시지 양 증가나 컨슈머 수 증가를 고려하지 않고 파티션 수를 적게 잡았다면,
나중에 파티션 수를 늘리게 되어 리밸런싱 중 다운타임이 발생할 수 있습니다.
⚠️ 부적절한 파티셔닝 키 선택 (Choosing the Wrong Partitioning Key)
효과적으로 데이터를 분산시키지 못하는 키를 사용하면, 메시지들이 특정 파티션에만 몰리게 됩니다.
결과적으로 일부 파티션은 과부하(hot partitions) 상태가 되고,
반대로 일부 파티션은 거의 사용되지 않는 **유휴 상태(idle)**가 됩니다.
이러한 불균형한 파티션 분배는 컨슈머 간의 워크로드 불균형을 초래하고,
전체 처리 효율을 떨어뜨립니다.
게다가, 메시지 순서 보장이 중요한 경우,
잘못된 키는 파티션 간 순서를 혼란스럽게 만들 수 있습니다.
(※ Kafka는 각 파티션 내에서는 순서를 보장하지만, 파티션 간에는 순서 보장이 되지 않음)
7.1. Tips to avoid pitfalls
🔹 적게 시작하고 점진적으로 확장하기 (Start low, scale up)
처음에는 보수적인 파티션 수로 시작하고,
모니터링 데이터와 성능 지표를 기반으로 점차 확장해 나가는 것이 좋습니다.
일반적으로 3~10개의 파티션은 시작점으로 적절합니다.
🔹 의미 있는 키 사용 (Meaningful keys)
null 키는 사용을 피해야 합니다.
**랜덤 키(UUID 등)**는 메시지 순서 보장이 필요하지 않을 때만 사용해야 합니다.
**접근 패턴과 메시지 순서 요구 조건(ordering guarantees)**을 고려하여,
메시지를 효과적으로 분산시킬 수 있는 키를 선택하세요.
🔹 모니터링 (Monitoring)
마지막으로, Kafka의 파티션 지연(partition lag), 컨슈머 오프셋, 브로커 자원 사용량 등
핵심 메트릭을 지속적으로 모니터링해야 합니다.
Kafka Monitor, Burrow, 또는 Prometheus + Grafana와 같은 도구를 사용하면
이상 징후를 더 빠르게 감지할 수 있습니다.
또한, 파티션별 메시지 분포를 분석하여 잠재적인 문제를 사전에 파악하세요.
'데이터 엔지니어링 정복 > Kafka' 카테고리의 다른 글
[Kafka] 9. Zookeeper (0) | 2025.05.02 |
---|---|
[Kafka] 8. Offset (0) | 2025.05.02 |
[Kafka] 6. Topics (1) | 2025.04.30 |
[Kafka] 5. Cluster (2) | 2025.04.29 |
[Kafka] 4. Broker (0) | 2025.04.29 |