이번 포스팅에서는 Kafka에 대해 심플하게 짚고 넘어가려고 합니다.
Apache Kafka
는 2011년도 LinkedIn 에서 사내 중앙집중형 분산 메세징 시스템으로 시작하여 현재는 대표적인 실시간 데이터 스트리밍 플랫폼으로 자리잡았습니다.
하위 프로젝트로는 KSQL
과 Kafka Streams
를 가지고 있습니다.
KSQL
Kafka Streams
Apache Kafka
와 같은 메세징 시스템(ActiveMQ, RabbitMQ, …)들은 시스템 내부에서 각 어플리케이션 간의 메세지 교환을 위해 만들어졌습니다. 이런 메세징 시스템은 아래와 같은 특징들을 지닙니다.
느슨한 결합
으로 이루어져 있습니다. 독자적인 시스템과 프로세스로 이루어져 있어 종속성이 약합니다.사실
Ack
나Exactly Once
등 정말 많은 내용들이 있지만 카프카 핵심 가이드(대규모 실시간 데이터와 스트림 처리) 포스팅에서 제대로 다루도록 하겠습니다.
%%{ init : { "theme" : "dark", "flowchart" : { "curve" : "stepBefore" }}}%%
graph LR
PD1([Producer 1])
PD2([Producer 2])
subgraph KC[Kafka Cluster > Broker]
subgraph TP3[Topic N]
PT1([Partition 1])
PT2([Partition 2])
...([. . .])
PTN([Partition N])
end
end
CS1([Consumer 1])
CS2([Consumer 2])
CS3([Consumer 3])
%% Data Flow
PD1 -- message --> PT1
PD1 -- "message" --> PT2
PD2 -- "message" --> PTN
PT1 -. "message" .- CS1
PT1 -. "message" .- CS2
PT2 -. "message" .- CS1
PT2 -. "message" .- CS2
PTN -. "message" .- CS3
위 플로우차트는 mermaid를 활용하여 Kafka의 대략적인 동작방식을 나타낸 것입니다. 간단하게 요약하자면 Producer
에서 broker
로 메세지를 보내면, broker
는 이를 스트리밍 버스(Streaming Bus)
와 같이 버퍼에 가지고 있다가 수많은 시스템에서 이 메세지들을 consume
해 가게 됩니다. 또한 이런 kafka 동작을 보조하는 분산코디네이터인 zookeeper
가 있습니다.
앞으로 다루게 될 카프카 포스팅에서는 이를 대체하는 KRaft
에 대해서도 함꼐 알어보도록 하겠습니다.
본론으로 돌아와서, Kafka를 이루는 구조적 특징을 몇가지 정리하고 넘어가겠습니다.
Topic(토픽)
에 저장합니다. 이 Topic
은 논리적인 개념의 메세지 주소를 의미하며, 사용자는 원하는 토픽(메세지 주소)로 메세지를 발송하고, Consumer는 이로부터 메세지를 수령할 수 있습니다.Offset
은 Partition
안에서 데이터의 위치를 표시하는 값을 의미합니다. Partition에 데이터가 저장되면 Kafka는 해당 Partition 안에서 위치값을 지정하고, Consumer는 Polling 방식으로 이 Offset(위치값)
을 읽어 메세지를 수신하게 됩니다.지난 카프카 도서들(실전 카프카 개발부터 운영까지, ]ㅡ,/ 아파치 카프카 애플리케이션 프로그래밍 with 자바)에 이어서 이번에 따끈따끈한 신판:카프카 핵심 가이드(대규모 실시간 데이터와 스트림 처리) 이 나와 제대로 읽어보며 포스팅을 진행하려고 합니다.
그러면 지금까지 배운 내용을 Java로 어떻게 구현할 수 있는지 확인해봅시다.
해당 공식문서 를 확인하면 Kafka의 세세한 Config 정보까지 전부 확인할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Producer API Sample Code
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.ACKS_CONFIG, "all");
props.put(ProducerConfig.RETRIES_CONFIG, Integer.valueOf(1));
props.put(ProducerConfig.LINGER_MS_CONFIG, Integer.valueOf(1));
// Consumer API Sample Code