Ver3.0 ๐ ์ค์๊ฐ ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐ: Kafka์ Flink๋ก ๋ฐ์ดํฐ ์ธ์ ์ ๋ณตํ๊ธฐ

๐ ์ค์๊ฐ ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐ: Kafka์ Flink๋ก ๋ฐ์ดํฐ ์ธ์ ์ ๋ณตํ๊ธฐ
๋น ๋ฐ์ดํฐ ์๋์ ํ์ ๋ฌด๊ธฐ, ์นดํ์นด์ ํ๋งํฌ๋ฅผ ์น๊ตฌ์ฒ๋ผ ์ฝ๊ฒ ์์๋ณด์!
๐ฏ ์ ์ง๊ธ ์ค์๊ฐ ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐ์ธ๊ฐ?
์ผ, ์์ฆ ์ธ์ ๋์๊ฐ๋ ๊ฑฐ ๋ดค์ด? ๋ทํ๋ฆญ์ค๋ ๋ด๊ฐ ๋ญ ์ข์ํ ์ง ์ค์๊ฐ์ผ๋ก ์์์ฑ๊ณ , ์ฐ๋ฒ๋ ๋ด ์์น์์ ๊ฐ์ฅ ๊ฐ๊น์ด ์ฐจ๋ฅผ ์ฐพ์์ฃผ๊ณ , ์ฟ ํก์ ๋ด๊ฐ ํด๋ฆญํ๋ ์๊ฐ ์ถ์ฒ ์ํ์ ๋ฐ๊ฟ๋ฒ๋ ค. ์ด๊ฒ ๋ค ์ค์๊ฐ ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐ ๋๋ถ์ด์ผ! โก
์์ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ชจ์๋๋ค๊ฐ ๋์ค์ ์ฒ๋ฆฌํ๋ '๋ฐฐ์น ์ฒ๋ฆฌ' ๋ฐฉ์์ด ๋์ธ์์ด. ํ๋ฃจ์ ํ ๋ฒ์ฉ ๋ฐค์ ๋๋ฆฌ๋ ๊ฑฐ์ง. ๊ทผ๋ฐ ์ง๊ธ์? 1์ด๊ฐ ๊ธํด! ๊ณ ๊ฐ์ด ํด๋ฆญํ๋ ์๊ฐ, ์ผ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ์๊ฐ, ๊ทธ ์ฆ์ ์ฒ๋ฆฌํด์ผ ๊ฒฝ์์์ ์ด์๋จ์ ์ ์์ด.
โข ๊ธ์ต ๊ฑฐ๋ ์ฌ๊ธฐ ํ์ง (0.1์ด ์์ ํ๋จํด์ผ ํจ!)
โข ์ค์๊ฐ ์ถ์ฒ ์์คํ (ํด๋ฆญํ๋ ์๊ฐ ์ทจํฅ ๋ถ์)
โข IoT ์ผ์ ๋ชจ๋ํฐ๋ง (๊ณต์ฅ ์ค๋น ์ด์ ์ฆ์ ๊ฐ์ง)
โข ์ค์๊ฐ ๋์๋ณด๋ (CEO๊ฐ ์ง๊ธ ๋น์ฅ ๋ณด๊ณ ์ถ์ด ํจ)
โข ๋ก๊ทธ ๋ถ์ ๋ฐ ์๋ฆผ (์๋ฒ ํฐ์ง๊ธฐ ์ ์ ์์์ผ์ง!)
๊ทธ๋์ ์ค๋์ ์ด ์ค์๊ฐ ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐ์ ์๋ ์ฐ๋งฅ, Apache Kafka์ Apache Flink์ ๋ํด ๊น์ด ํํค์ณ๋ณผ ๊ฑฐ์ผ. ์ฌ๋ฅ๋ท์์๋ ์ด๋ฐ ๊ธฐ์ ์ ๋ค๋ฃจ๋ ์ ๋ฌธ๊ฐ๋ค์ด ๋ง์๋ฐ, ์ด ๊ธ ์ฝ๊ณ ๋๋ฉด ๋๋ ๊ทธ๋ค๊ณผ ๋ํํ ์ ์์ ๊ฑฐ์ผ! ๐
๐ฆ Apache Kafka: ๋ฐ์ดํฐ ๊ณ ์๋๋ก์ ์
๐ช Kafka๊ฐ ๋ญ๊ธธ๋?
์นดํ์นด๋ ๋งํฌ๋์ธ์์ 2011๋ ์ ๋ง๋ ๋ถ์ฐ ๋ฉ์์ง ์์คํ ์ด์ผ. ์ฝ๊ฒ ๋งํ๋ฉด ์์ฒญ๋๊ฒ ๋น ๋ฅธ ์ฐ์ฒด๊ตญ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ. ์๋ง์ ๋ฐ์ ์(Producer)๊ฐ ํธ์ง(๋ฉ์์ง)๋ฅผ ๋ณด๋ด๋ฉด, ์นดํ์นด๊ฐ ์ด๊ฑธ ๋ฐ์์ ๋ถ๋ฅํ๊ณ , ์์ ์(Consumer)๋ค์๊ฒ ์ ๋ฌํด์ฃผ๋ ๊ฑฐ์ง.
๊ทผ๋ฐ ์ผ๋ฐ ์ฐ์ฒด๊ตญ๊ณผ ๋ค๋ฅธ ์ ์? ๐
1. ์ด๊ณ ์์ด์ผ! ์ด๋น ์๋ฐฑ๋ง ๊ฐ์ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ ์ ์์ด.
2. ์ ๋ ์์ด๋ฒ๋ฆฌ์ง ์์! ๋์คํฌ์ ์ ์ฅํ๋๊น ์๋ฒ๊ฐ ์ฃฝ์ด๋ ๋ฐ์ดํฐ๋ ์ด์์์ด.
3. ํ์ฅ์ด ์ฌ์! ์๋ฒ ๋ช ๋ ๋ ์ถ๊ฐํ๋ฉด ์ฑ๋ฅ์ด ์ญ์ญ ์ฌ๋ผ๊ฐ.
4. ์ฌ์ ๊ฐ๋ฅํด! ๊ณผ๊ฑฐ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ์ฝ์ ์ ์์ด. ํ์๋จธ์ ๊ฐ์ ๊ฑฐ์ง.
โข Netflix: ํ๋ฃจ 7์กฐ ๊ฐ ์ด์์ ๋ฉ์์ง ์ฒ๋ฆฌ
โข LinkedIn: ์ค์๊ฐ ํ๋ ์คํธ๋ฆผ ์ฒ๋ฆฌ
โข Uber: ์ค์๊ฐ ์์น ์ถ์ ๋ฐ ์๊ธ ๊ณ์ฐ
โข Airbnb: ์ค์๊ฐ ๊ฐ๊ฒฉ ์ต์ ํ
โข ๊ตญ๋ด ๋ํ ํฌํธ, ๊ธ์ต์ฌ ๋ค์
๐ง Kafka์ ํต์ฌ ๊ฐ๋ ๋ค
์, ์ด์ ์นดํ์นด์ ๋ด๋ถ๋ฅผ ๋ค์ฌ๋ค๋ณผ ์๊ฐ์ด์ผ. ์ฒ์์ ์ข ํท๊ฐ๋ฆด ์ ์๋๋ฐ, ์ฒ์ฒํ ๋ฐ๋ผ์๋ด! ๐งญ
1. Topic (ํ ํฝ)
ํ ํฝ์ ๋ฉ์์ง๋ฅผ ๋ถ๋ฅํ๋ ์นดํ ๊ณ ๋ฆฌ์ผ. ์๋ฅผ ๋ค์ด '์ฃผ๋ฌธ ๋ฐ์ดํฐ', 'ํด๋ฆญ ๋ก๊ทธ', '๊ฒฐ์ ์ ๋ณด' ์ด๋ฐ ์์ผ๋ก ๋๋ . ์ฐ์ฒด๊ตญ์ ์ฐํธํจ ๊ฐ์ ๊ฑฐ๋ผ๊ณ ๋ณด๋ฉด ๋ผ.
// ํ ํฝ ์์ฑ ์์
kafka-topics.sh --create \
--topic user-clicks \
--bootstrap-server localhost:9092 \
--partitions 3 \
--replication-factor 2
2. Partition (ํํฐ์ )
ํ๋์ ํ ํฝ์ ์ฌ๋ฌ ์กฐ๊ฐ์ผ๋ก ๋๋ ๊ฑฐ์ผ. ์ ๋๋๋๊ณ ? ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ์ํด์! ํํฐ์ ์ด 3๊ฐ๋ฉด 3๊ฐ์ ์๋ฒ๊ฐ ๋์์ ์ฒ๋ฆฌํ ์ ์์ด์ 3๋ฐฐ ๋นจ๋ผ์ง๋ ๊ฑฐ์ง. ๊ฐ ํํฐ์ ์ ์์๊ฐ ๋ณด์ฅ๋๋ ํ(Queue)์ผ.
3. Producer (ํ๋ก๋์)
๋ฐ์ดํฐ๋ฅผ ์นดํ์นด์ ๋ณด๋ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด์ผ. ์น ์๋ฒ, ๋ชจ๋ฐ์ผ ์ฑ, IoT ์ผ์ ๋ฑ์ด ํ๋ก๋์๊ฐ ๋ ์ ์์ด.
// Java๋ก ์์ฑํ ๊ฐ๋จํ Producer ์์
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
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);
ProducerRecord<String, String> record =
new ProducerRecord<>("user-clicks", "user123", "clicked-product-456");
producer.send(record);
producer.close();
4. Consumer (์ปจ์๋จธ)
์นดํ์นด์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด๊ฐ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด์ผ. ์ฌ๋ฌ ์ปจ์๋จธ๊ฐ ํ๋์ ํ ํฝ์ ๋์์ ์ฝ์ ์ ์์ด. ๊ฐ์ ์๊ธฐ ์๋๋ก ์ฝ์ผ๋ฉด ๋ผ.
// Consumer ์์
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "analytics-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("user-clicks"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf("User: %s, Action: %s%n", record.key(), record.value());
}
}
5. Consumer Group (์ปจ์๋จธ ๊ทธ๋ฃน)
์ฌ๋ฌ ์ปจ์๋จธ๋ฅผ ํ๋์ ๊ทธ๋ฃน์ผ๋ก ๋ฌถ์ ๊ฑฐ์ผ. ๊ฐ์ ๊ทธ๋ฃน ๋ด์ ์ปจ์๋จธ๋ค์ ํํฐ์ ์ ๋๋ ์ ์ฝ์ด. ์๋ฅผ ๋ค์ด ํํฐ์ ์ด 6๊ฐ๊ณ ์ปจ์๋จธ๊ฐ 3๊ฐ๋ฉด, ๊ฐ ์ปจ์๋จธ๊ฐ 2๊ฐ์ฉ ๋ด๋นํ๋ ๊ฑฐ์ง. ์ด๋ ๊ฒ ํ๋ฉด ์ฒ๋ฆฌ ์๋๊ฐ ๋นจ๋ผ์ ธ! โก
6. Broker (๋ธ๋ก์ปค)
์นดํ์นด ์๋ฒ ํ ๋๋ฅผ ๋ธ๋ก์ปค๋ผ๊ณ ๋ถ๋ฌ. ๋ณดํต ์ฌ๋ฌ ๋์ ๋ธ๋ก์ปค๋ฅผ ํด๋ฌ์คํฐ๋ก ๋ฌถ์ด์ ์ด์ํด. ํ ๋๊ฐ ์ฃฝ์ด๋ ๋ค๋ฅธ ๋ธ๋ก์ปค๊ฐ ๋์ ์ผํ๋๊น ์์ ํ์ง!
7. Zookeeper (์ฃผํคํผ)
์นดํ์นด ํด๋ฌ์คํฐ๋ฅผ ๊ด๋ฆฌํ๋ ์ฝ๋๋ค์ดํฐ์ผ. ์ด๋ค ๋ธ๋ก์ปค๊ฐ ์ด์์๋์ง, ๋ฆฌ๋๋ ๋๊ตฌ์ธ์ง ๋ฑ์ ๊ด๋ฆฌํด. ์ต๊ทผ์๋ KRaft ๋ชจ๋๋ก ์ฃผํคํผ ์์ด๋ ์ด์ํ ์ ์๊ฒ ๋์ด!
์นดํ์นด ์ค์ ์ด๋ ์ด์์ด ์ด๋ ต๋ค๋ฉด, ์ฌ๋ฅ๋ท์์ ์นดํ์นด ์ ๋ฌธ๊ฐ๋ฅผ ์ฐพ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ๊ฐ๋ฐ์๋ค์ด ์ปจ์คํ ์ด๋ ๊ต์ก์ ์ ๊ณตํ๊ณ ์๊ฑฐ๋ !
๐ฏ Kafka์ ํต์ฌ ํน์ง๊ณผ ์ฅ์
1. ๋์ ์ฒ๋ฆฌ๋ (High Throughput)
์นดํ์นด๋ ๋์คํฌ I/O๋ฅผ ์ต์ ํํด์ ์ด๋น ์๋ฐฑ๋ง ๊ฐ์ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ ์ ์์ด. ๋น๊ฒฐ์ ์์ฐจ ์ฐ๊ธฐ(Sequential Write)์ผ. ๋์คํฌ์ ์์๋๋ก ์ญ ์ฐ๋ฉด ๋๋ค ์ฐ๊ธฐ๋ณด๋ค ํจ์ฌ ๋นจ๋ผ. SSD๊ฐ ์๋ ์ผ๋ฐ HDD๋ก๋ ์ถฉ๋ถํ ๋น ๋ฅธ ์ฑ๋ฅ์ ๋ผ ์ ์์ด!
2. ๋ด๊ตฌ์ฑ (Durability)
๋ฉ์์ง๋ฅผ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์๋ ๋์คํฌ์ ์ ์ฅํด. ๊ทธ๋ฆฌ๊ณ ์ฌ๋ฌ ๋ธ๋ก์ปค์ ๋ณต์ (Replication)ํด๋๋๊น, ์๋ฒ ํ๋ ๋๊ฐ ์ฃฝ์ด๋ ๋ฐ์ดํฐ๋ ์์ ํด. ๊ธ์ต๊ถ์์๋ ์ฐ๋ ์ด์ ๊ฐ ์ด๊ฑฐ์ผ! ๐ก๏ธ
3. ํ์ฅ์ฑ (Scalability)
ํธ๋ํฝ์ด ๋์ด๋๋ฉด? ๋ธ๋ก์ปค ๋ช ๋ ๋ ์ถ๊ฐํ๋ฉด ๋! ํํฐ์ ์๋ฅผ ๋๋ฆฌ๋ฉด ๋ณ๋ ฌ ์ฒ๋ฆฌ ๋ฅ๋ ฅ๋ ์ญ์ญ ์ฌ๋ผ๊ฐ. ์ํ ํ์ฅ(Horizontal Scaling)์ด ์ ๋ง ์ฌ์.
4. ๋ฉ์์ง ์์ ๋ณด์ฅ
๊ฐ์ ํํฐ์ ๋ด์์๋ ๋ฉ์์ง ์์๊ฐ ๋ณด์ฅ๋ผ. ์ฃผ๋ฌธ ์ฒ๋ฆฌ ๊ฐ์ ๊ณณ์์ ์์๊ฐ ์ค์ํ์์? ์นดํ์นด๋ ์ด๊ฑธ ์๋ฒฝํ๊ฒ ์ง์ผ์ค.
5. ์ฌ์ ๊ฐ๋ฅ (Replayable)
๊ณผ๊ฑฐ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ์ฝ์ ์ ์์ด. ๋ฒ๊ทธ ์์ ํ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฒ๋ฆฌํ๊ฑฐ๋, ์๋ก์ด ๋ถ์ ๋ก์ง์ ๊ณผ๊ฑฐ ๋ฐ์ดํฐ์ ์ ์ฉํ ๋ ์์ฒญ ์ ์ฉํด!
| ํน์ง | ์ค๋ช | ์ค๋ฌด ํ์ฉ |
|---|---|---|
| ์ฒ๋ฆฌ๋ | ์ด๋น ์๋ฐฑ๋ง ๋ฉ์์ง | ๋์ฉ๋ ๋ก๊ทธ ์์ง |
| ์ง์ฐ์๊ฐ | ๋ฐ๋ฆฌ์ด ๋จ์ | ์ค์๊ฐ ์๋ฆผ, ๋ชจ๋ํฐ๋ง |
| ๋ณด๊ด ๊ธฐ๊ฐ | ์ค์ ๊ฐ๋ฅ (์ผ, ์ฃผ, ์) | ๋ฐ์ดํฐ ์ฌ์ฒ๋ฆฌ, ๊ฐ์ฌ |
| ๋ณต์ | ๋ค์ค ๋ธ๋ก์ปค ๋ณต์ | ๊ณ ๊ฐ์ฉ์ฑ ๋ณด์ฅ |
| ํํฐ์ ๋ | ๋ณ๋ ฌ ์ฒ๋ฆฌ | ์ฑ๋ฅ ์ต์ ํ |
๐ Kafka ์ค์ ํ์ฉ ์ฌ๋ก
์ฌ๋ก 1: ์ค์๊ฐ ๋ก๊ทธ ์์ง
์๋ฐฑ ๋์ ์น ์๋ฒ์์ ๋ฐ์ํ๋ ๋ก๊ทธ๋ฅผ ์นดํ์นด๋ก ๋ชจ์. ๊ทธ๋ผ ์ค์์์ ํ ๋ฒ์ ๋ถ์ํ ์ ์์ง. ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ์ฆ์ ์๋ฆผ์ ๋ณด๋ผ ์๋ ์๊ณ !
์ฌ๋ก 2: ์ด๋ฒคํธ ์์ฑ (Event Sourcing)
๋ชจ๋ ์ํ ๋ณ๊ฒฝ์ ์ด๋ฒคํธ๋ก ๊ธฐ๋กํด. ์๋ฅผ ๋ค์ด '์ฃผ๋ฌธ ์์ฑ', '๊ฒฐ์ ์๋ฃ', '๋ฐฐ์ก ์์' ๊ฐ์ ์ด๋ฒคํธ๋ค์ ์นดํ์นด์ ์ ์ฅํ๋ฉด, ๋์ค์ ์ ์ฒด ํ๋ฆ์ ์ฌ๊ตฌ์ฑํ ์ ์์ด.
์ฌ๋ก 3: ๋ง์ดํฌ๋ก์๋น์ค ๊ฐ ํต์
์ฃผ๋ฌธ ์๋น์ค, ๊ฒฐ์ ์๋น์ค, ๋ฐฐ์ก ์๋น์ค๊ฐ ์นดํ์นด๋ฅผ ํตํด ๋น๋๊ธฐ๋ก ํต์ ํด. ํ ์๋น์ค๊ฐ ๋๋ ค์ ธ๋ ๋ค๋ฅธ ์๋น์ค์ ์ํฅ์ ์ ์ค. ๋์จํ ๊ฒฐํฉ(Loose Coupling)์ด ๊ฐ๋ฅํด์ง๋ ๊ฑฐ์ง! ๐
์ฌ๋ก 4: CDC (Change Data Capture)
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณ๊ฒฝ ์ฌํญ์ ์ค์๊ฐ์ผ๋ก ์บก์ฒํด์ ์นดํ์นด๋ก ๋ณด๋ด. ๊ทธ๋ผ ๋ค๋ฅธ ์์คํ ๋ค์ด ์ด ๋ณ๊ฒฝ ์ฌํญ์ ์ค์๊ฐ์ผ๋ก ๋ฐ์ํ ์ ์์ด. ๋ฐ์ดํฐ ๋๊ธฐํ๊ฐ ์์ฒญ ์ฌ์์ ธ!
๐ Apache Flink: ์คํธ๋ฆผ ์ฒ๋ฆฌ์ ์ต๊ฐ์
๐ญ Flink๋ ๋ ๋ญ์ผ?
์นดํ์นด๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ์ฐ์ฒด๊ตญ์ด๋ผ๋ฉด, ํ๋งํฌ๋ ๊ทธ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ณต์ฅ์ด์ผ. ๋ ์ผ ๋ฒ ๋ฅผ๋ฆฐ ๊ณต๊ณผ๋ํ์์ ์์๋ ํ๋ก์ ํธ์ธ๋ฐ, ์ง๊ธ์ ์ํ์น ์ต์์ ํ๋ก์ ํธ๋ก ์๋ฆฌ์ก์์ด.
ํ๋งํฌ์ ๊ฐ์ฅ ํฐ ํน์ง์? ๐
1. ์ง์ง ์คํธ๋ฆผ ์ฒ๋ฆฌ์ผ! ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๋ ์ฆ์ ์ฒ๋ฆฌํด. ๋ง์ดํฌ๋ก ๋ฐฐ์น๊ฐ ์๋๋ผ ์ง์ง ์คํธ๋ฆฌ๋ฐ์ด์ผ.
2. ์ ํํ ํ ๋ฒ ์ฒ๋ฆฌ ๋ณด์ฅ! Exactly-once semantics๋ฅผ ์ง์ํด. ์ค๋ณต๋ ์๊ณ ๋๋ฝ๋ ์์ด.
3. ์ํ ๊ด๋ฆฌ๊ฐ ๋ฐ์ด๋! ๋ณต์กํ ๊ณ์ฐ ์ํ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ ์งํ๋ฉด์๋ ์ฅ์ ๋ณต๊ตฌ๊ฐ ๊ฐ๋ฅํด.
4. ์ด๋ฒคํธ ์๊ฐ ์ฒ๋ฆฌ! ๋ฐ์ดํฐ๊ฐ ์ค์ ๋ก ๋ฐ์ํ ์๊ฐ ๊ธฐ์ค์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ด.
๋ง์ ์ฌ๋๋ค์ด Spark Streaming๊ณผ ๋น๊ตํ๋๋ฐ, ํต์ฌ ์ฐจ์ด๋ ์ด๊ฑฐ์ผ:
โข Spark Streaming: ๋ง์ดํฌ๋ก ๋ฐฐ์น ๋ฐฉ์. ์์ ๋ฐฐ์น๋ฅผ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํด์ ์ค์๊ฐ์ฒ๋ผ ๋ณด์ด๊ฒ ํจ.
โข Flink: ์ง์ง ์คํธ๋ฆผ ์ฒ๋ฆฌ. ๋ ์ฝ๋ ๋จ์๋ก ์ฆ์ ์ฒ๋ฆฌ.
๊ฒฐ๊ณผ์ ์ผ๋ก Flink๊ฐ ์ง์ฐ์๊ฐ์ด ๋ ๋ฎ๊ณ , ๋ณต์กํ ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๋ ์ ํฉํด!
๐ Flink์ ํต์ฌ ๊ฐ๋ ๋ค
1. DataStream API
ํ๋งํฌ์ ํต์ฌ API์ผ. ์คํธ๋ฆผ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ๋ชจ๋ ์ฐ์ฐ์ ์ ๊ณตํด. map, filter, reduce, window ๋ฑ๋ฑ.
// Flink DataStream ์์
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// Kafka์์ ๋ฐ์ดํฐ ์ฝ๊ธฐ
FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>(
"user-clicks",
new SimpleStringSchema(),
properties
);
DataStream<String> stream = env.addSource(consumer);
// ๋ฐ์ดํฐ ์ฒ๋ฆฌ
DataStream<Tuple2<String, Integer>> result = stream
.map(value -> new Tuple2<>(value, 1))
.keyBy(0)
.timeWindow(Time.minutes(1))
.sum(1);
// ๊ฒฐ๊ณผ ์ถ๋ ฅ
result.print();
env.execute("Flink Kafka Example");
2. Window (์๋์ฐ)
๋ฌดํํ ์คํธ๋ฆผ์ ์ ํํ ์กฐ๊ฐ์ผ๋ก ๋๋๋ ๋ฐฉ๋ฒ์ด์ผ. ์๋ฅผ ๋ค์ด "์ต๊ทผ 5๋ถ๊ฐ์ ํด๋ฆญ ์"๋ฅผ ๊ณ์ฐํ๋ ค๋ฉด 5๋ถ ์๋์ฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฑฐ์ง.
์๋์ฐ ์ข ๋ฅ๊ฐ ์ฌ๋ฌ ๊ฐ์ง์ผ:
โข Tumbling Window: ๊ฒน์น์ง ์๋ ๊ณ ์ ํฌ๊ธฐ ์๋์ฐ (์: ๋งค 1๋ถ๋ง๋ค)
โข Sliding Window: ๊ฒน์น๋ ์๋์ฐ (์: 5๋ถ ์๋์ฐ๋ฅผ 1๋ถ๋ง๋ค ๊ณ์ฐ)
โข Session Window: ํ๋์ด ์์ผ๋ฉด ์๋์ฐ๊ฐ ๋ซํ (์: ์ฌ์ฉ์ ์ธ์ ๋ถ์)
// Tumbling Window ์์
stream
.keyBy(value -> value.getUserId())
.window(TumblingEventTimeWindows.of(Time.minutes(5)))
.sum("amount");
// Sliding Window ์์
stream
.keyBy(value -> value.getUserId())
.window(SlidingEventTimeWindows.of(Time.minutes(10), Time.minutes(5)))
.sum("amount");
// Session Window ์์
stream
.keyBy(value -> value.getUserId())
.window(EventTimeSessionWindows.withGap(Time.minutes(30)))
.sum("amount");
3. Time (์๊ฐ)
ํ๋งํฌ๋ ์ธ ๊ฐ์ง ์๊ฐ ๊ฐ๋ ์ ์ง์ํด:
โข Event Time: ์ด๋ฒคํธ๊ฐ ์ค์ ๋ก ๋ฐ์ํ ์๊ฐ. ๊ฐ์ฅ ์ ํํ์ง๋ง ์ฒ๋ฆฌ๊ฐ ๋ณต์กํด.
โข Ingestion Time: ํ๋งํฌ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์๊ฐ.
โข Processing Time: ํ๋งํฌ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ์๊ฐ. ๊ฐ์ฅ ๊ฐ๋จํ์ง๋ง ๋ถ์ ํํ ์ ์์ด.
์ค๋ฌด์์๋ ๋ณดํต Event Time์ ์จ. ๋คํธ์ํฌ ์ง์ฐ์ด๋ ์์คํ ์ฅ์ ๊ฐ ์์ด๋ ์ ํํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์๊ฑฐ๋ ! โฐ
4. Watermark (์ํฐ๋งํฌ)
์ด๋ฒคํธ ํ์์ ์ธ ๋ ํ์ํ ๊ฐ๋ ์ด์ผ. "์ด ์๊ฐ ์ด์ ์ ๋ฐ์ดํฐ๋ ๋ ์ด์ ์ ์ฌ ๊ฑฐ์ผ"๋ผ๊ณ ์๋ ค์ฃผ๋ ์ ํธ์ผ. ๋ฆ๊ฒ ๋์ฐฉํ๋ ๋ฐ์ดํฐ(Late Data)๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง ๊ฒฐ์ ํ๋ ๋ฐ ์ฌ์ฉ๋ผ.
// Watermark ์ค์ ์์
DataStream<Event> withTimestampsAndWatermarks = stream
.assignTimestampsAndWatermarks(
WatermarkStrategy
.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(10))
.withTimestampAssigner((event, timestamp) -> event.getTimestamp())
);
5. State (์ํ)
ํ๋งํฌ์ ์ง์ง ๊ฐ์ ์ด์ผ! ์ฒ๋ฆฌ ์ค๊ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํ ์ ์์ด. ์๋ฅผ ๋ค์ด "์ฌ์ฉ์๋ณ ๋์ ๊ตฌ๋งค ๊ธ์ก"์ ๊ณ์ฐํ ๋, ๊ฐ ์ฌ์ฉ์์ ํ์ฌ ๊ธ์ก์ ์ํ๋ก ์ ์งํ๋ ๊ฑฐ์ง.
์ํ ์ข ๋ฅ:
โข Keyed State: ํค๋ณ๋ก ๊ด๋ฆฌ๋๋ ์ํ (์: ์ฌ์ฉ์๋ณ ์นด์ดํฐ)
โข Operator State: ์คํผ๋ ์ดํฐ ์ ์ฒด๊ฐ ๊ณต์ ํ๋ ์ํ
// Keyed State ์ฌ์ฉ ์์
public class CountFunction extends RichFlatMapFunction<Event, Tuple2<String, Long>> {
private transient ValueState<Long> count;
@Override
public void open(Configuration config) {
ValueStateDescriptor<Long> descriptor =
new ValueStateDescriptor<>("count", Long.class, 0L);
count = getRuntimeContext().getState(descriptor);
}
@Override
public void flatMap(Event event, Collector<Tuple2<String, Long>> out) throws Exception {
Long currentCount = count.value();
currentCount += 1;
count.update(currentCount);
out.collect(new Tuple2<>(event.getUserId(), currentCount));
}
}
6. Checkpoint (์ฒดํฌํฌ์ธํธ)
ํ๋งํฌ๋ ์ฃผ๊ธฐ์ ์ผ๋ก ์ํ๋ฅผ ์ค๋ ์ท์ผ๋ก ์ ์ฅํด. ์ฅ์ ๊ฐ ๋ฐ์ํ๋ฉด ๋ง์ง๋ง ์ฒดํฌํฌ์ธํธ๋ถํฐ ์ฌ์์ํ๋ฉด ๋ผ. ์ด๊ฒ ๋ฐ๋ก Exactly-once๋ฅผ ๋ณด์ฅํ๋ ๋น๊ฒฐ์ด์ผ! ๐พ
// Checkpoint ์ค์
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 5์ด๋ง๋ค ์ฒดํฌํฌ์ธํธ
env.enableCheckpointing(5000);
// Exactly-once ๋ชจ๋
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
// ์ฒดํฌํฌ์ธํธ ํ์์์ 1๋ถ
env.getCheckpointConfig().setCheckpointTimeout(60000);
// ๋์ ์ฒดํฌํฌ์ธํธ 1๊ฐ๋ง
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
๐ฏ Flink์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค
1. CEP (Complex Event Processing)
๋ณต์กํ ์ด๋ฒคํธ ํจํด์ ๊ฐ์งํ ์ ์์ด. ์๋ฅผ ๋ค์ด "๋ก๊ทธ์ธ ์คํจ 3๋ฒ ํ ์ฑ๊ณต"์ด๋ผ๋ ํจํด์ ์ฐพ์๋ผ ์ ์์ง.
// CEP ํจํด ์ ์
Pattern<LoginEvent, ?> pattern = Pattern.<LoginEvent>begin("first")
.where(new SimpleCondition<LoginEvent>() {
@Override
public boolean filter(LoginEvent event) {
return event.getStatus().equals("fail");
}
})
.times(3)
.next("second")
.where(new SimpleCondition<LoginEvent>() {
@Override
public boolean filter(LoginEvent event) {
return event.getStatus().equals("success");
}
})
.within(Time.minutes(5));
// ํจํด ๋งค์นญ
PatternStream<LoginEvent> patternStream = CEP.pattern(loginStream, pattern);
2. Table API & SQL
SQL๋ก ์คํธ๋ฆผ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์์ด! ๋ฐฐ์น ๋ฐ์ดํฐ ๋ค๋ฃจ๋ฏ์ด ์คํธ๋ฆผ์ ๋ค๋ฃฐ ์ ์์ด์ ์ง์ ์ฅ๋ฒฝ์ด ๋ฎ์์ ธ.
// Table API ์์
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
// ์คํธ๋ฆผ์ ํ
์ด๋ธ๋ก ๋ณํ
tableEnv.createTemporaryView("Clicks", clickStream, $("user"), $("url"), $("timestamp"));
// SQL ์ฟผ๋ฆฌ
Table result = tableEnv.sqlQuery(
"SELECT user, COUNT(*) as cnt " +
"FROM Clicks " +
"GROUP BY TUMBLE(timestamp, INTERVAL '1' MINUTE), user"
);
// ๋ค์ ์คํธ๋ฆผ์ผ๋ก ๋ณํ
DataStream<Row> resultStream = tableEnv.toDataStream(result);
3. Async I/O
์ธ๋ถ ์์คํ (DB, API ๋ฑ)์ ์กฐํํ ๋ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ ์ ์์ด. ์ฑ๋ฅ์ด ์์ฒญ ์ข์์ ธ!
// Async I/O ์์
class AsyncDatabaseRequest extends RichAsyncFunction<String, Tuple2<String, String>> {
private transient DatabaseClient client;
@Override
public void open(Configuration parameters) throws Exception {
client = new DatabaseClient();
}
@Override
public void asyncInvoke(String key, ResultFuture<Tuple2<String, String>> resultFuture) {
CompletableFuture<String> future = client.query(key);
future.thenAccept(result -> {
resultFuture.complete(Collections.singleton(new Tuple2<>(key, result)));
});
}
}
// ์ ์ฉ
DataStream<Tuple2<String, String>> result = AsyncDataStream.unorderedWait(
stream,
new AsyncDatabaseRequest(),
1000, TimeUnit.MILLISECONDS,
100
);
4. Side Output
๋ฉ์ธ ์คํธ๋ฆผ์์ ํน์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฆฌํด๋ผ ์ ์์ด. ์๋ฅผ ๋ค์ด ์๋ฌ ๋ฐ์ดํฐ๋ง ๋ฐ๋ก ์ฒ๋ฆฌํ๊ณ ์ถ์ ๋ ์ ์ฉํด! ๐
// Side Output ์์
final OutputTag<Event> lateDataTag = new OutputTag<Event>("late-data"){};
SingleOutputStreamOperator<Event> mainStream = stream
.process(new ProcessFunction<Event, Event>() {
@Override
public void processElement(Event event, Context ctx, Collector<Event> out) {
if (event.getTimestamp() < ctx.timerService().currentWatermark()) {
// ๋ฆ์ ๋ฐ์ดํฐ๋ ์ฌ์ด๋ ์์ํ์ผ๋ก
ctx.output(lateDataTag, event);
} else {
// ์ ์ ๋ฐ์ดํฐ๋ ๋ฉ์ธ ์คํธ๋ฆผ์ผ๋ก
out.collect(event);
}
}
});
// ๋ฆ์ ๋ฐ์ดํฐ ์คํธ๋ฆผ ๊ฐ์ ธ์ค๊ธฐ
DataStream<Event> lateStream = mainStream.getSideOutput(lateDataTag);
๐ Kafka + Flink: ์ต๊ฐ ์กฐํฉ
์, ์ด์ ์นดํ์นด์ ํ๋งํฌ๋ฅผ ํจ๊ป ์ฐ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์! ์ด ๋์ ์ ๋ง ํ์์ ๊ถํฉ์ด์ผ. ๐ฏ
๐๏ธ ์ ์ฒด ์ํคํ ์ฒ
์ ํ์ ์ธ ์ค์๊ฐ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ์ ์ด๋ ๊ฒ ๊ตฌ์ฑ๋ผ:
1๋จ๊ณ: ๋ฐ์ดํฐ ์์ง
โข ์น ์๋ฒ, ๋ชจ๋ฐ์ผ ์ฑ, IoT ์ผ์ ๋ฑ์์ ๋ฐ์ดํฐ ๋ฐ์
โข Producer๊ฐ Kafka๋ก ๋ฐ์ดํฐ ์ ์ก
2๋จ๊ณ: ๋ฐ์ดํฐ ๋ฒํผ๋ง
โข Kafka๊ฐ ๋ฐ์ดํฐ๋ฅผ Topic์ ์ ์ฅ
โข ์ฌ๋ฌ Consumer๊ฐ ๋์์ ์ฝ์ ์ ์๋๋ก ์ค๋น
3๋จ๊ณ: ์ค์๊ฐ ์ฒ๋ฆฌ
โข Flink๊ฐ Kafka์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ด
โข ๋ณํ, ํํฐ๋ง, ์ง๊ณ, ์กฐ์ธ ๋ฑ ๋ณต์กํ ์ฒ๋ฆฌ ์ํ
4๋จ๊ณ: ๊ฒฐ๊ณผ ์ ์ฅ
โข ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ Kafka๋ก ๋ณด๋ด๊ฑฐ๋
โข DB, Elasticsearch, S3 ๋ฑ์ ์ ์ฅ
๐ป ์ค์ ์์ : ์ค์๊ฐ ํด๋ฆญ ์คํธ๋ฆผ ๋ถ์
์ ์์๊ฑฐ๋ ์ฌ์ดํธ์์ ์ฌ์ฉ์ ํด๋ฆญ์ ์ค์๊ฐ์ผ๋ก ๋ถ์ํ๋ ์์คํ ์ ๋ง๋ค์ด๋ณด์!
์๊ตฌ์ฌํญ:
โข ์ฌ์ฉ์๋ณ ์ต๊ทผ 1๋ถ๊ฐ ํด๋ฆญ ์ ๊ณ์ฐ
โข ์ํ๋ณ ์ธ๊ธฐ๋ ์ค์๊ฐ ์ง๊ณ
โข ์ด์ ํจํด ๊ฐ์ง (๋ด ํธ๋ํฝ ๋ฑ)
// 1. ํ๊ฒฝ ์ค์
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
env.enableCheckpointing(5000);
// 2. Kafka Source ์ค์
Properties kafkaProps = new Properties();
kafkaProps.setProperty("bootstrap.servers", "localhost:9092");
kafkaProps.setProperty("group.id", "flink-consumer");
FlinkKafkaConsumer<ClickEvent> consumer = new FlinkKafkaConsumer<>(
"user-clicks",
new ClickEventSchema(),
kafkaProps
);
// Watermark ์ค์ (10์ด ์ง์ฐ ํ์ฉ)
consumer.assignTimestampsAndWatermarks(
WatermarkStrategy
.<ClickEvent>forBoundedOutOfOrderness(Duration.ofSeconds(10))
.withTimestampAssigner((event, timestamp) -> event.getTimestamp())
);
// 3. ๋ฐ์ดํฐ ์คํธ๋ฆผ ์์ฑ
DataStream<ClickEvent> clickStream = env.addSource(consumer);
// 4. ์ฌ์ฉ์๋ณ 1๋ถ๊ฐ ํด๋ฆญ ์ ๊ณ์ฐ
DataStream<Tuple2<String, Long>> userClickCounts = clickStream
.keyBy(ClickEvent::getUserId)
.window(TumblingEventTimeWindows.of(Time.minutes(1)))
.aggregate(new CountAggregator());
// 5. ์ํ๋ณ ์ธ๊ธฐ๋ ๊ณ์ฐ
DataStream<Tuple2<String, Long>> productPopularity = clickStream
.filter(event -> event.getEventType().equals("product_view"))
.keyBy(ClickEvent::getProductId)
.window(SlidingEventTimeWindows.of(Time.minutes(5), Time.minutes(1)))
.aggregate(new CountAggregator());
// 6. ์ด์ ํจํด ๊ฐ์ง (1๋ถ์ 100๋ฒ ์ด์ ํด๋ฆญ)
DataStream<Alert> alerts = userClickCounts
.filter(tuple -> tuple.f1 > 100)
.map(tuple -> new Alert(tuple.f0, "Suspicious activity detected"));
// 7. ๊ฒฐ๊ณผ๋ฅผ Kafka๋ก ๋ค์ ์ ์ก
FlinkKafkaProducer<Alert> producer = new FlinkKafkaProducer<>(
"alerts",
new AlertSerializationSchema(),
kafkaProps,
FlinkKafkaProducer.Semantic.EXACTLY_ONCE
);
alerts.addSink(producer);
// 8. ์คํ
env.execute("Real-time Click Stream Analysis");
โข Watermark: 10์ด๊น์ง ๋ฆ๊ฒ ๋์ฐฉํ๋ ๋ฐ์ดํฐ๋ฅผ ํ์ฉ
โข Tumbling Window: ๊ฒน์น์ง ์๋ 1๋ถ ์๋์ฐ๋ก ํด๋ฆญ ์ ๊ณ์ฐ
โข Sliding Window: 5๋ถ ์๋์ฐ๋ฅผ 1๋ถ๋ง๋ค ๊ณ์ฐํด์ ํธ๋ ๋ ํ์
โข Exactly-once: ์ค๋ณต ์์ด ์ ํํ ํ ๋ฒ๋ง ์ฒ๋ฆฌ ๋ณด์ฅ
โข Checkpoint: 5์ด๋ง๋ค ์ํ ์ ์ฅ์ผ๋ก ์ฅ์ ๋ณต๊ตฌ ๊ฐ๋ฅ
๐จ ๊ณ ๊ธ ํจํด: ์คํธ๋ฆผ ์กฐ์ธ
์ค๋ฌด์์๋ ์ฌ๋ฌ ์คํธ๋ฆผ์ ์กฐ์ธํด์ผ ํ ๋๊ฐ ๋ง์. ์๋ฅผ ๋ค์ด ํด๋ฆญ ์คํธ๋ฆผ๊ณผ ๊ตฌ๋งค ์คํธ๋ฆผ์ ์กฐ์ธํด์ ์ ํ์จ์ ๊ณ์ฐํ๋ ๊ฑฐ์ง!
// ํด๋ฆญ ์คํธ๋ฆผ
DataStream<ClickEvent> clicks = env.addSource(clickConsumer);
// ๊ตฌ๋งค ์คํธ๋ฆผ
DataStream<PurchaseEvent> purchases = env.addSource(purchaseConsumer);
// ์คํธ๋ฆผ ์กฐ์ธ (๊ฐ์ ์ฌ์ฉ์์ ํด๋ฆญ๊ณผ ๊ตฌ๋งค๋ฅผ 10๋ถ ๋ด์ ๋งค์นญ)
DataStream<ConversionEvent> conversions = clicks
.keyBy(ClickEvent::getUserId)
.intervalJoin(purchases.keyBy(PurchaseEvent::getUserId))
.between(Time.minutes(-10), Time.minutes(0))
.process(new ConversionProcessFunction());
// ์ ํ์จ ๊ณ์ฐ
DataStream<Double> conversionRate = conversions
.windowAll(TumblingEventTimeWindows.of(Time.hours(1)))
.aggregate(new ConversionRateAggregator());
๐ฅ ์ฑ๋ฅ ์ต์ ํ ํ
1. ๋ณ๋ ฌ๋(Parallelism) ์กฐ์
Kafka ํํฐ์ ์์ Flink ๋ณ๋ ฌ๋๋ฅผ ๋ง์ถฐ์ค. ํํฐ์ ์ด 10๊ฐ๋ฉด ๋ณ๋ ฌ๋๋ 10์ผ๋ก ์ค์ ํ๋ ๊ฒ ์ข์.
env.setParallelism(10);
// ํน์ ์คํผ๋ ์ดํฐ๋ง ๋ณ๋ ฌ๋ ์กฐ์
stream.map(...).setParallelism(5);
2. ์ํ ๋ฐฑ์๋ ์ ํ
์ํ๊ฐ ํฌ๋ฉด RocksDB ๋ฐฑ์๋๋ฅผ ์จ. ๋ฉ๋ชจ๋ฆฌ๋ณด๋ค ๋๋ฆฌ์ง๋ง ๋์คํฌ๋ฅผ ์ฐ๋๊น ์ฉ๋ ์ ํ์ด ์์ด.
env.setStateBackend(new RocksDBStateBackend("hdfs://namenode:9000/flink/checkpoints"));
3. ์ฒดํฌํฌ์ธํธ ๊ฐ๊ฒฉ ์กฐ์
๋๋ฌด ์์ฃผ ํ๋ฉด ์ค๋ฒํค๋๊ฐ ํฌ๊ณ , ๋๋ฌด ๋๋ฌผ๋ฉด ์ฅ์ ์ ๋ณต๊ตฌ ์๊ฐ์ด ๊ธธ์ด์ ธ. ๋ณดํต 5~10์ด๊ฐ ์ ๋นํด.
4. ๋คํธ์ํฌ ๋ฒํผ ํ๋
๋ฐ์ดํฐ ์ ์ก๋์ด ๋ง์ผ๋ฉด ๋คํธ์ํฌ ๋ฒํผ๋ฅผ ๋๋ ค์ค.
taskmanager.network.memory.fraction: 0.2
taskmanager.network.memory.min: 128mb
taskmanager.network.memory.max: 1gb
5. ๋ฐฑํ๋ ์ (Backpressure) ๋ชจ๋ํฐ๋ง
Flink Web UI์์ ๋ฐฑํ๋ ์ ๋ฅผ ํ์ธํด. ํน์ ์คํผ๋ ์ดํฐ๊ฐ ๋ณ๋ชฉ์ด๋ฉด ๋ณ๋ ฌ๋๋ฅผ ๋์ด๊ฑฐ๋ ๋ก์ง์ ์ต์ ํํด์ผ ํด! ๐
๐ ๏ธ ์ค์ ์ด์ ๋ ธํ์ฐ
์ด๋ก ์ ์ด์ ์ถฉ๋ถํด! ์ค์ ๋ก ์ด์ํ ๋ ์์์ผ ํ ๊ฒ๋ค์ ์๋ ค์ค๊ฒ.
๐จ ๋ชจ๋ํฐ๋ง๊ณผ ์๋ฆผ
Kafka ๋ชจ๋ํฐ๋ง ์งํ
โข Under-replicated Partitions: ๋ณต์ ๊ฐ ์ ๋ ํํฐ์ ์. 0์ด์ด์ผ ์ ์!
โข Offline Partitions: ์คํ๋ผ์ธ ํํฐ์ ์. ์ ๋ 0์ด์ด์ผ ํจ!
โข ISR Shrink/Expand Rate: ๋ณต์ ๋ณธ ์ํ ๋ณํ์จ. ๋๋ฌด ์์ฃผ ๋ฐ๋๋ฉด ๋ฌธ์
โข Bytes In/Out: ์ด๋น ์ ์ก๋. ๊ธ์ฆํ๋ฉด ์ฉ๋ ์ฆ์ค ๊ณ ๋ ค
โข Consumer Lag: ์ปจ์๋จธ๊ฐ ์ผ๋ง๋ ๋ค์ฒ์ก๋์ง. ๊ณ์ ์ฆ๊ฐํ๋ฉด ์ํ!
// Consumer Lag ํ์ธ
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--describe --group my-group
GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG
my-group user-clicks 0 1000 1500 500
my-group user-clicks 1 2000 2100 100
Flink ๋ชจ๋ํฐ๋ง ์งํ
โข Checkpoint Duration: ์ฒดํฌํฌ์ธํธ ์์ ์๊ฐ. ๋๋ฌด ๊ธธ๋ฉด ๋ฌธ์
โข Checkpoint Failure Rate: ์คํจ์จ. 0์ ๊ฐ๊น์์ผ ํจ
โข Records Processed: ์ฒ๋ฆฌ๋ ๋ ์ฝ๋ ์
โข Backpressure: ๋ฐฑํ๋ ์ ์ํ. HIGH๋ฉด ๋ณ๋ชฉ ํด๊ฒฐ ํ์
โข Task Failure Rate: ํ์คํฌ ์คํจ์จ
โข Prometheus + Grafana: ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์กฐํฉ. ๋ฌด๋ฃ๊ณ ๊ฐ๋ ฅํด!
โข Kafka Manager: ์นดํ์นด ํด๋ฌ์คํฐ ๊ด๋ฆฌ UI
โข Burrow: LinkedIn์ด ๋ง๋ Consumer Lag ๋ชจ๋ํฐ๋ง ๋๊ตฌ
โข Flink Dashboard: ๊ธฐ๋ณธ ์ ๊ณต๋๋ ์น UI. ์ค์๊ฐ ๋ฉํธ๋ฆญ ํ์ธ ๊ฐ๋ฅ
๐ง ์ฅ์ ๋์ ์๋๋ฆฌ์ค
์๋๋ฆฌ์ค 1: Kafka ๋ธ๋ก์ปค ๋ค์ด
โข ์ฆ์: ํน์ ํํฐ์ ์ ์ฐ๊ธฐ/์ฝ๊ธฐ ์คํจ
โข ์์ธ: ๋ธ๋ก์ปค ์ฅ์
โข ํด๊ฒฐ: ๋ณต์ ๋ณธ์ด ์์ผ๋ฉด ์๋์ผ๋ก ๋ค๋ฅธ ๋ธ๋ก์ปค๊ฐ ๋ฆฌ๋๊ฐ ๋จ. ๋ธ๋ก์ปค ์ฌ์์ ํ ๋ณต์ ์ฌ๊ฐ
์๋๋ฆฌ์ค 2: Consumer Lag ๊ธ์ฆ
โข ์ฆ์: ์ปจ์๋จธ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ผ์ก์ง ๋ชปํจ
โข ์์ธ: ์ฒ๋ฆฌ ์๋ < ์์ฐ ์๋
โข ํด๊ฒฐ: ์ปจ์๋จธ ์ธ์คํด์ค ์ถ๊ฐ, ํํฐ์ ์ ์ฆ๊ฐ, ์ฒ๋ฆฌ ๋ก์ง ์ต์ ํ
์๋๋ฆฌ์ค 3: Flink Job ์คํจ
โข ์ฆ์: Job์ด ๊ณ์ ์ฌ์์๋จ
โข ์์ธ: ์ฝ๋ ๋ฒ๊ทธ, ๋ฆฌ์์ค ๋ถ์กฑ, ์ธ๋ถ ์์คํ ์ฅ์
โข ํด๊ฒฐ: ๋ก๊ทธ ํ์ธ, ์ฒดํฌํฌ์ธํธ์์ ๋ณต๊ตฌ, ๋ฆฌ์์ค ์ฆ์ค
์๋๋ฆฌ์ค 4: ๋์คํฌ ๊ณต๊ฐ ๋ถ์กฑ
โข ์ฆ์: ์นดํ์นด๊ฐ ์ ๋ฉ์์ง๋ฅผ ๋ฐ์ง ๋ชปํจ
โข ์์ธ: ๋ก๊ทธ ๋ณด๊ด ๊ธฐ๊ฐ์ด ๋๋ฌด ๊ธธ๊ฑฐ๋ ํธ๋ํฝ ๊ธ์ฆ
โข ํด๊ฒฐ: ์ค๋๋ ๋ก๊ทธ ์ญ์ , ๋ณด๊ด ๊ธฐ๊ฐ ๋จ์ถ, ๋์คํฌ ์ฆ์ค
// ๋ก๊ทธ ๋ณด๊ด ๊ธฐ๊ฐ ์ค์ (7์ผ)
log.retention.hours=168
// ๋ก๊ทธ ํฌ๊ธฐ ์ ํ (10GB)
log.retention.bytes=10737418240
// ๋ก๊ทธ ์ธ๊ทธ๋จผํธ ํฌ๊ธฐ (1GB)
log.segment.bytes=1073741824
๐ฏ ๋ฐฐํฌ ์ ๋ต
๋ธ๋ฃจ-๊ทธ๋ฆฐ ๋ฐฐํฌ
์ ๋ฒ์ ์ ๋ณ๋ ํด๋ฌ์คํฐ์ ๋ฐฐํฌํ๊ณ , ํธ๋ํฝ์ ์ ์ง์ ์ผ๋ก ์ด๋์์ผ. ๋ฌธ์ ์๊ธฐ๋ฉด ์ฆ์ ๋กค๋ฐฑ ๊ฐ๋ฅ!
์นด๋๋ฆฌ ๋ฐฐํฌ
์ผ๋ถ ํธ๋ํฝ๋ง ์ ๋ฒ์ ์ผ๋ก ๋ณด๋ด์ ํ ์คํธํด. ๋ฌธ์ ์์ผ๋ฉด ์ ์ง์ ์ผ๋ก ํ๋.
Savepoint ํ์ฉ
Flink๋ Savepoint๋ฅผ ์ฐ์ด๋๋ฉด ๋์ค์ ์ ํํ ๊ทธ ์์ ๋ถํฐ ์ฌ์์ํ ์ ์์ด. ์ ๊ทธ๋ ์ด๋ํ ๋ ํ์์ผ!
// Savepoint ์์ฑ
flink savepoint <jobId> [targetDirectory]
// Savepoint์์ ์ฌ์์
flink run -s :savepointPath [:runArgs]
๐ฐ ๋น์ฉ ์ต์ ํ
1. ์ ์ ํ ๋ณด๊ด ๊ธฐ๊ฐ ์ค์
์นดํ์นด ๋ก๊ทธ๋ฅผ ๋ฌดํ์ ๋ณด๊ดํ๋ฉด ๋์คํฌ ๋น์ฉ์ด ํญ๋ฐํด. ํ์ํ ๋งํผ๋ง ๋ณด๊ดํด.
2. ์์ถ ํ์ฉ
๋ฉ์์ง๋ฅผ ์์ถํ๋ฉด ๋คํธ์ํฌ ๋์ญํญ๊ณผ ๋์คํฌ ๊ณต๊ฐ์ ์ ์ฝํ ์ ์์ด.
compression.type=snappy // ๋๋ lz4, gzip, zstd
3. ๋ฆฌ์์ค Right-sizing
๊ณผ๋ํ ๋ฆฌ์์ค ํ ๋น์ ๋ญ๋น์ผ. ๋ชจ๋ํฐ๋ง ๋ฐ์ดํฐ๋ฅผ ๋ณด๊ณ ์ ์ ์์ค์ผ๋ก ์กฐ์ ํด.
4. ์คํ ์ธ์คํด์ค ํ์ฉ
ํด๋ผ์ฐ๋์์๋ ์คํ ์ธ์คํด์ค๋ฅผ ์ฐ๋ฉด ๋น์ฉ์ 70% ์ด์ ์ ๊ฐํ ์ ์์ด. ๋จ, ์ค์ํ ๋ธ๋ก์ปค๋ ์จ๋๋งจ๋๋ก! ๐ต
๐ ์ค์ ์ฌ๋ก ์ฐ๊ตฌ
๐ฑ ์ฌ๋ก 1: ๋ํ ์์ ๋ฏธ๋์ด ํ๋ซํผ
๋ฌธ์ : ํ๋ฃจ ์์ญ์ต ๊ฑด์ ์ฌ์ฉ์ ํ๋ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ฒ๋ฆฌํด์ ์ถ์ฒ ์์คํ ์ ๋ฐ์ํด์ผ ํจ.
์๋ฃจ์ :
โข Kafka ํด๋ฌ์คํฐ: 100๋ ์ด์์ ๋ธ๋ก์ปค
โข ํํฐ์ : ํ ํฝ๋น 1000๊ฐ ์ด์
โข Flink ํด๋ฌ์คํฐ: ์๋ฐฑ ๋์ TaskManager
โข ์ฒ๋ฆฌ๋: ์ด๋น 1000๋ง ์ด๋ฒคํธ ์ด์
ํต์ฌ ๊ธฐ์ :
โข ์ฌ์ฉ์๋ณ ์ค์๊ฐ ํ๋กํ ์ ๋ฐ์ดํธ (Keyed State ํ์ฉ)
โข ๋ณต์กํ ์ด๋ฒคํธ ํจํด ๊ฐ์ง (CEP ํ์ฉ)
โข ๋ค์ค ์คํธ๋ฆผ ์กฐ์ธ (์น๊ตฌ ๊ด๊ณ, ๊ด์ฌ์ฌ ๋ฑ)
โข ์ค์๊ฐ A/B ํ ์คํธ ๊ฒฐ๊ณผ ์ง๊ณ
๊ฒฐ๊ณผ:
โข ์ถ์ฒ ์ ํ๋ 30% ํฅ์
โข ์ฌ์ฉ์ ์ฐธ์ฌ๋ 25% ์ฆ๊ฐ
โข ์ธํ๋ผ ๋น์ฉ 40% ์ ๊ฐ (๊ธฐ์กด ๋ฐฐ์น ์์คํ ๋๋น)
๐ฆ ์ฌ๋ก 2: ๊ธ๋ก๋ฒ ๊ธ์ต ๊ธฐ๊ด
๋ฌธ์ : ์ค์๊ฐ ์ฌ๊ธฐ ๊ฑฐ๋ ํ์ง. ๋ฐ๋ฆฌ์ด ๋จ์๋ก ํ๋จํด์ผ ํจ.
์๋ฃจ์ :
โข Kafka: ๊ฑฐ๋ ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐ
โข Flink: ์ค์๊ฐ ์ด์ ํจํด ๊ฐ์ง
โข ๋จธ์ ๋ฌ๋ ๋ชจ๋ธ ํตํฉ (Async I/O๋ก ๋ชจ๋ธ ์๋ฒ ํธ์ถ)
ํต์ฌ ๋ก์ง:
โข ์ฌ์ฉ์๋ณ ์ต๊ทผ ๊ฑฐ๋ ํจํด ํ์ต (State ํ์ฉ)
โข ์ง๋ฆฌ์ ์์น ๊ธฐ๋ฐ ์ด์ ํ์ง
โข ๊ฑฐ๋ ๊ธ์ก ํจํด ๋ถ์
โข ๋ณตํฉ ๊ท์น ์์ง (CEP ํ์ฉ)
๊ฒฐ๊ณผ:
โข ์ฌ๊ธฐ ํ์ง์จ 95% ์ด์
โข ์คํ๋ฅ 50% ๊ฐ์
โข ํ๊ท ํ์ง ์๊ฐ 100ms ์ดํ
โข ์ฐ๊ฐ ์๋ฐฑ์ต ์์ ์์ค ๋ฐฉ์ง
๐ ์ฌ๋ก 3: ์ฐจ๋ ๊ณต์ ์๋น์ค
๋ฌธ์ : ์๋ฐฑ๋ง ๋์ ์ฐจ๋ ์์น๋ฅผ ์ค์๊ฐ์ผ๋ก ์ถ์ ํ๊ณ , ์ต์ ์ ๋งค์นญ์ ์ ๊ณตํด์ผ ํจ.
์๋ฃจ์ :
โข Kafka: ์ฐจ๋ ์์น, ์ฌ์ฉ์ ์์ฒญ ์คํธ๋ฆฌ๋ฐ
โข Flink: ์ค์๊ฐ ์ง๋ฆฌ ๊ณต๊ฐ ๋ถ์
โข ๋์ ๊ฐ๊ฒฉ ์ฑ ์ ์๊ณ ๋ฆฌ์ฆ
์ํคํ ์ฒ:
// ์ฐจ๋ ์์น ์คํธ๋ฆผ
DataStream<VehicleLocation> vehicles = env.addSource(vehicleConsumer);
// ์ฌ์ฉ์ ์์ฒญ ์คํธ๋ฆผ
DataStream<RideRequest> requests = env.addSource(requestConsumer);
// ์ง๋ฆฌ์ ๊ทธ๋ฆฌ๋๋ก ๋ถํ (GeoHash ์ฌ์ฉ)
DataStream<Tuple2<String, VehicleLocation>> vehiclesByGrid = vehicles
.map(v -> new Tuple2<>(GeoHash.encode(v.getLat(), v.getLon(), 6), v));
DataStream<Tuple2<String, RideRequest>> requestsByGrid = requests
.map(r -> new Tuple2<>(GeoHash.encode(r.getLat(), r.getLon(), 6), r));
// ๊ฐ์ ๊ทธ๋ฆฌ๋ ๋ด์์ ๋งค์นญ
DataStream<Match> matches = vehiclesByGrid
.keyBy(0)
.connect(requestsByGrid.keyBy(0))
.process(new MatchingFunction());
// ๋์ ๊ฐ๊ฒฉ ๊ณ์ฐ
DataStream<PriceUpdate> prices = requests
.keyBy(r -> r.getRegion())
.window(TumblingEventTimeWindows.of(Time.minutes(5)))
.aggregate(new DynamicPricingAggregator());
๊ฒฐ๊ณผ:
โข ํ๊ท ๋๊ธฐ ์๊ฐ 40% ๊ฐ์
โข ์ฐจ๋ ํ์ฉ๋ฅ 30% ์ฆ๊ฐ
โข ๋์ ๊ฐ๊ฒฉ ์ฑ ์ ์ผ๋ก ์์ต 20% ์ฆ๊ฐ
โข ์์คํ ์์ ์ฑ 99.99% ๋ฌ์ฑ
๐ญ ์ฌ๋ก 4: ์ค๋งํธ ํฉํ ๋ฆฌ
๋ฌธ์ : ์์ฒ ๊ฐ์ IoT ์ผ์ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ถ์ํด์ ์ค๋น ์ด์์ ์กฐ๊ธฐ์ ๊ฐ์งํด์ผ ํจ.
์๋ฃจ์ :
โข Kafka: ์ผ์ ๋ฐ์ดํฐ ์์ง (์จ๋, ์ง๋, ์๋ ฅ ๋ฑ)
โข Flink: ์ค์๊ฐ ์ด์ ํ์ง ๋ฐ ์์ธก ์ ์ง๋ณด์
โข ์๊ณ์ด ๋ถ์ ๋ฐ ๋จธ์ ๋ฌ๋ ๋ชจ๋ธ ์ ์ฉ
ํต์ฌ ๊ธฐ๋ฅ:
โข ์ผ์ ๋ฐ์ดํฐ ์ ๊ทํ ๋ฐ ํํฐ๋ง
โข ์ด๋ ํ๊ท ๊ณ์ฐ (Sliding Window)
โข ์๊ณ๊ฐ ๊ธฐ๋ฐ ์๋ฆผ
โข ํจํด ๊ธฐ๋ฐ ๊ณ ์ฅ ์์ธก (CEP)
๊ฒฐ๊ณผ:
โข ์ค๋น ๊ฐ๋๋ฅ 15% ํฅ์
โข ์๋ฐฉ ์ ๋น๋ก ๊ณ ์ฅ 60% ๊ฐ์
โข ์ ์ง๋ณด์ ๋น์ฉ 35% ์ ๊ฐ
โข ์์ฐ์ฑ 25% ์ฆ๊ฐ
์ด๋ฐ ์ค์ ์ฌ๋ก๋ค์ ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด, ์ฌ๋ฅ๋ท์์ ์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ์ ๋ฌธ๊ฐ๋ค์ ๋ฉํ ๋ง์ ๋ฐ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ์ฑ ์ด๋ ๊ฐ์๋ก๋ ๋ฐฐ์ธ ์ ์๋ ์ค์ ๋ ธํ์ฐ๋ฅผ ์ป์ ์ ์๊ฑฐ๋ !
๐ ๋ฏธ๋ ํธ๋ ๋์ ๋ฐ์ ๋ฐฉํฅ
๐ฎ Kafka์ ๋ฏธ๋
1. KRaft ๋ชจ๋ (Zookeeper ์ ๊ฑฐ)
Kafka 2.8๋ถํฐ Zookeeper ์์ด๋ ์ด์ํ ์ ์๊ฒ ๋์ด. KRaft(Kafka Raft)๋ผ๋ ์์ฒด ํฉ์ ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ ๊ฑฐ์ง. ๋ ๊ฐ๋จํ๊ณ ํ์ฅ์ฑ๋ ์ข์์ ธ!
2. Tiered Storage
์ค๋๋ ๋ฐ์ดํฐ๋ฅผ S3 ๊ฐ์ ์ ๋ ดํ ์คํ ๋ฆฌ์ง๋ก ์๋์ผ๋ก ์ฎ๊ฒจ. ๋น์ฉ์ ์ค์ด๋ฉด์ ์ฅ๊ธฐ ๋ณด๊ด์ด ๊ฐ๋ฅํด์ ธ.
3. Kafka Streams ๊ฐ์
Kafka ์์ฒด์ ํฌํจ๋ ์คํธ๋ฆผ ์ฒ๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ. Flink๋ณด๋ค ๊ฐ๋ณ๊ณ ๊ฐ๋จํด์ ์ธ๊ธฐ๊ฐ ๋์์ง๊ณ ์์ด.
4. ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ
Confluent Cloud, Amazon MSK ๊ฐ์ ๋งค๋์ง๋ ์๋น์ค๊ฐ ๋ฐ์ ํ๊ณ ์์ด. ์ด์ ๋ถ๋ด ์์ด ์ธ ์ ์์ด! โ๏ธ
๐ Flink์ ๋ฏธ๋
1. Flink SQL ๊ฐํ
SQL๋ง์ผ๋ก ๋ณต์กํ ์คํธ๋ฆผ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๊ฒ ๋์ด. ์ง์ ์ฅ๋ฒฝ์ด ๋ฎ์์ง๋ ๊ฑฐ์ง!
// Flink SQL๋ก ์คํธ๋ฆผ ์ฒ๋ฆฌ
CREATE TABLE clicks (
user_id STRING,
product_id STRING,
event_time TIMESTAMP(3),
WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
'connector' = 'kafka',
'topic' = 'user-clicks',
'properties.bootstrap.servers' = 'localhost:9092'
);
-- 1๋ถ ์๋์ฐ๋ก ํด๋ฆญ ์ ๊ณ์ฐ
SELECT
user_id,
COUNT(*) as click_count,
TUMBLE_START(event_time, INTERVAL '1' MINUTE) as window_start
FROM clicks
GROUP BY
user_id,
TUMBLE(event_time, INTERVAL '1' MINUTE);
2. ๋จธ์ ๋ฌ๋ ํตํฉ
Flink ML์ด ๋ฐ์ ํ๋ฉด์ ์คํธ๋ฆผ ๋ฐ์ดํฐ์ ๋จธ์ ๋ฌ๋์ ๋ฐ๋ก ์ ์ฉํ ์ ์๊ฒ ๋์ด.
3. Stateful Functions
์๋ฒ๋ฆฌ์ค ์คํ์ผ๋ก ์ํ๋ฅผ ๊ฐ์ง ํจ์๋ฅผ ์์ฑํ ์ ์์ด. ๋ง์ดํฌ๋ก์๋น์ค์ ์ ์ด์ธ๋ ค!
4. Kubernetes ๋ค์ดํฐ๋ธ
Kubernetes์์ Flink๋ฅผ ์ฝ๊ฒ ๋ฐฐํฌํ๊ณ ๊ด๋ฆฌํ ์ ์๊ฒ ๋์ด. ์คํ ์ค์ผ์ผ๋ง๋ ์ง์ํด!
๐ฏ ์๋ก์ด ํจ๋ฌ๋ค์
1. Lakehouse ์ํคํ ์ฒ
๋ฐ์ดํฐ ๋ ์ดํฌ์ ๋ฐ์ดํฐ ์จ์ดํ์ฐ์ค์ ์ฅ์ ์ ๊ฒฐํฉํ ๊ฑฐ์ผ. Kafka + Flink + Delta Lake ์กฐํฉ์ด ์ธ๊ธฐ!
2. Real-time OLAP
์ค์๊ฐ์ผ๋ก ๋ณต์กํ ๋ถ์ ์ฟผ๋ฆฌ๋ฅผ ์ํํ๋ ๊ฑฐ์ผ. Apache Druid, ClickHouse ๊ฐ์ ๋๊ตฌ๋ค๊ณผ ํตํฉ๋ผ.
3. Edge Computing
IoT ๊ธฐ๊ธฐ์์ ์ง์ ์คํธ๋ฆผ ์ฒ๋ฆฌ๋ฅผ ํ๋ ๊ฑฐ์ผ. ์ง์ฐ์๊ฐ์ ๊ทน๋๋ก ์ค์ผ ์ ์์ด!
4. AI/ML ํ์ดํ๋ผ์ธ
์ค์๊ฐ ํน์ง ์ถ์ถ, ๋ชจ๋ธ ์๋น, ์จ๋ผ์ธ ํ์ต๊น์ง ๋ชจ๋ ์คํธ๋ฆฌ๋ฐ์ผ๋ก! ๐ค
๐ ํ์ต ๋ก๋๋งต
์, ์ด์ ์ด๋ป๊ฒ ๊ณต๋ถํด์ผ ํ ์ง ์๋ ค์ค๊ฒ!
๐ ์ด๊ธ ๋จ๊ณ (1-2๊ฐ์)
1์ฃผ์ฐจ: ๊ธฐ์ด ๊ฐ๋
โข ๋ฉ์์ง ์์คํ ์ด๋?
โข ์คํธ๋ฆผ ์ฒ๋ฆฌ vs ๋ฐฐ์น ์ฒ๋ฆฌ
โข Kafka ์ํคํ ์ฒ ์ดํด
โข ๊ฐ๋จํ Producer/Consumer ์์ฑ
2-3์ฃผ์ฐจ: Kafka ์ค์ต
โข ๋ก์ปฌ์ Kafka ์ค์น
โข Topic, Partition ๊ฐ๋ ์ค์ต
โข Consumer Group ๋์ ํ์ธ
โข ๊ฐ๋จํ ๋ก๊ทธ ์์ง ์์คํ ๊ตฌ์ถ
4-6์ฃผ์ฐจ: Flink ์ ๋ฌธ
โข Flink ์ค์น ๋ฐ ์คํ
โข DataStream API ๊ธฐ์ด
โข ๊ฐ๋จํ ๋ณํ ์์ (map, filter)
โข Kafka์ ์ฐ๋
7-8์ฃผ์ฐจ: ์ฒซ ํ๋ก์ ํธ
โข ์ค์๊ฐ ๋จ์ด ์นด์ดํธ
โข ๊ฐ๋จํ ๋ก๊ทธ ๋ถ์
โข ๊ธฐ๋ณธ ๋ชจ๋ํฐ๋ง ์ค์
๐ ์ค๊ธ ๋จ๊ณ (3-4๊ฐ์)
Window ์ฐ์ฐ
โข Tumbling, Sliding, Session Window
โข Event Time vs Processing Time
โข Watermark ์ดํด ๋ฐ ์ค์
์ํ ๊ด๋ฆฌ
โข Keyed State ํ์ฉ
โข Checkpoint ์ค์
โข Savepoint ํ์ฉ
๊ณ ๊ธ ์ฐ์ฐ
โข ์คํธ๋ฆผ ์กฐ์ธ
โข CEP (Complex Event Processing)
โข Async I/O
์ด์ ๊ธฐ์ด
โข ๋ชจ๋ํฐ๋ง ์ค์
โข ๋ก๊ทธ ๋ถ์
โข ๊ฐ๋จํ ์ฅ์ ๋์
๐ ๊ณ ๊ธ ๋จ๊ณ (6๊ฐ์ ์ด์)
์ฑ๋ฅ ์ต์ ํ
โข ๋ณ๋ ฌ๋ ํ๋
โข ๋คํธ์ํฌ ๋ฒํผ ์ต์ ํ
โข ์ํ ๋ฐฑ์๋ ์ ํ
โข ๋ฐฑํ๋ ์ ํด๊ฒฐ
๋๊ท๋ชจ ์์คํ ์ค๊ณ
โข ๋ฉํฐ ํด๋ฌ์คํฐ ์ํคํ ์ฒ
โข ์ฌํด ๋ณต๊ตฌ ์ ๋ต
โข ๋ณด์ ์ค์
โข ๋น์ฉ ์ต์ ํ
๊ณ ๊ธ ํจํด
โข Lambda ์ํคํ ์ฒ
โข Kappa ์ํคํ ์ฒ
โข Event Sourcing
โข CQRS ํจํด
์ค์ ํ๋ก์ ํธ
โข ์ค์๊ฐ ์ถ์ฒ ์์คํ
โข ์ฌ๊ธฐ ํ์ง ์์คํ
โข ์ค์๊ฐ ๋์๋ณด๋
โข IoT ๋ฐ์ดํฐ ๋ถ์
๊ณต์ ๋ฌธ์
โข Kafka Documentation: kafka.apache.org
โข Flink Documentation: flink.apache.org
์ฑ
โข "Kafka: The Definitive Guide" - Neha Narkhede
โข "Stream Processing with Apache Flink" - Fabian Hueske
โข "Designing Data-Intensive Applications" - Martin Kleppmann
์จ๋ผ์ธ ๊ฐ์
โข Confluent Developer Courses
โข Udemy Kafka & Flink ๊ฐ์
โข YouTube ํํ ๋ฆฌ์ผ
์ปค๋ฎค๋ํฐ
โข Kafka Users ๋ฉ์ผ๋ง ๋ฆฌ์คํธ
โข Flink User Mailing List
โข Stack Overflow
โข ๊ตญ๋ด ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ
๐ก ์ค๋ฌด ํ๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค
โ Kafka ๋ฒ ์คํธ ํ๋ํฐ์ค
1. ์ ์ ํ ํํฐ์ ์ ์ ํ
โข ์ฒ๋ฆฌ๋ ๊ธฐ์ค: ๋ชฉํ ์ฒ๋ฆฌ๋ / ํํฐ์ ๋น ์ฒ๋ฆฌ๋
โข ๋ณดํต ๋ธ๋ก์ปค๋น 2000-4000๊ฐ ํํฐ์ ์ด ์ ๋น
โข ๋๋ฌด ๋ง์ผ๋ฉด ์ค๋ฒํค๋, ๋๋ฌด ์ ์ผ๋ฉด ๋ณ๋ ฌ์ฑ ๋ถ์กฑ
2. ๋ณต์ ํฉํฐ๋ ์ต์ 3
โข ํ๋ก๋์ ์์๋ replication.factor=3 ๊ถ์ฅ
โข min.insync.replicas=2๋ก ์ค์ ํด์ ์์ ์ฑ ํ๋ณด
3. ์์ถ ํ์ฉ
โข ๋คํธ์ํฌ ๋์ญํญ์ด ๋ณ๋ชฉ์ด๋ฉด ์์ถ ์ฌ์ฉ
โข snappy๋ lz4 ์ถ์ฒ (์๋์ ์์ถ๋ฅ ๊ท ํ)
4. ๋ฐฐ์น ํฌ๊ธฐ ์กฐ์
โข batch.size์ linger.ms ํ๋
โข ์ฒ๋ฆฌ๋ ์ค์ํ๋ฉด ํฌ๊ฒ, ์ง์ฐ์๊ฐ ์ค์ํ๋ฉด ์๊ฒ
5. ๋ฉฑ๋ฑ์ฑ ํ๋ก๋์ ์ฌ์ฉ
โข enable.idempotence=true ์ค์
โข ์ค๋ณต ๋ฉ์์ง ๋ฐฉ์ง
โ Flink ๋ฒ ์คํธ ํ๋ํฐ์ค
1. Event Time ์ฌ์ฉ
โข ๊ฐ๋ฅํ๋ฉด ํญ์ Event Time ์ฌ์ฉ
โข Processing Time์ ํ ์คํธ๋ ๊ฐ๋จํ ์ผ์ด์ค์๋ง
2. Watermark ์ ์คํ๊ฒ ์ค์
โข ๋๋ฌด ์งง์ผ๋ฉด ๋ฆ์ ๋ฐ์ดํฐ ๋๋ฝ
โข ๋๋ฌด ๊ธธ๋ฉด ๊ฒฐ๊ณผ ์ง์ฐ
โข ๋ฐ์ดํฐ ํน์ฑ ํ์ ํ ๊ฒฐ์
3. ์ํ ํฌ๊ธฐ ๊ด๋ฆฌ
โข ์ํ๊ฐ ๋ฌดํ์ ์ปค์ง์ง ์๋๋ก ์ฃผ์
โข TTL ์ค์ ์ผ๋ก ์ค๋๋ ์ํ ์๋ ์ญ์
โข ํ์ํ๋ฉด RocksDB ๋ฐฑ์๋ ์ฌ์ฉ
// State TTL ์ค์ ์์
StateTtlConfig ttlConfig = StateTtlConfig
.newBuilder(Time.days(7))
.setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
.setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
.build();
ValueStateDescriptor<Long> descriptor = new ValueStateDescriptor<>("count", Long.class);
descriptor.enableTimeToLive(ttlConfig);
4. ๋ฆฌ์์ค ์ ์ ํ ํ ๋น
โข TaskManager ๋ฉ๋ชจ๋ฆฌ ์ถฉ๋ถํ ํ ๋น
โข ๋คํธ์ํฌ ๋ฒํผ ์ ์ ํ ์ค์
โข CPU ์ฝ์ด ์์ ๋ณ๋ ฌ๋ ๋ง์ถ๊ธฐ
5. ์ฒดํฌํฌ์ธํธ ์ต์ ํ
โข ์ฆ๋ถ ์ฒดํฌํฌ์ธํธ ์ฌ์ฉ (RocksDB)
โข ๋น๋๊ธฐ ์ฒดํฌํฌ์ธํธ ํ์ฑํ
โข ์ฒดํฌํฌ์ธํธ ๊ฐ๊ฒฉ ์ ์ ํ ์ค์
๐ซ ํผํด์ผ ํ ์ํฐํจํด
โ Kafka
โข ๋๋ฌด ํฐ ๋ฉ์์ง ๋ณด๋ด๊ธฐ (1MB ์ด์)
โข ํํฐ์ ํค ์์ด ๋ณด๋ด๊ธฐ (์์ ๋ณด์ฅ ์ ๋จ)
โข Consumer์์ ๋๊ธฐ ์ฒ๋ฆฌ (๋๋ ค์ง)
โข ๋ก๊ทธ ๋ณด๊ด ๊ธฐ๊ฐ ๋ฌดํ์ ์ค์
โ Flink
โข Processing Time์ผ๋ก ์ค์ํ ๊ณ์ฐํ๊ธฐ
โข ์ํ ํฌ๊ธฐ ์ ํ ์์ด ์ฌ์ฉ
โข ์ฒดํฌํฌ์ธํธ ์์ด ์ด์
โข ๋ฐฑํ๋ ์ ๋ฌด์ํ๊ธฐ
โข ์ธ๋ถ ์์คํ ๋๊ธฐ ํธ์ถ (Async I/O ์จ์ผ ํจ)
๐ฏ ์ฑ๋ฅ ํ๋ ์ฒดํฌ๋ฆฌ์คํธ
| ํญ๋ชฉ | ํ์ธ ์ฌํญ | ๊ถ์ฅ ๊ฐ |
|---|---|---|
| Kafka ํํฐ์ | ๋ณ๋ ฌ ์ฒ๋ฆฌ ๊ฐ๋ฅํ๊ฐ? | ์ปจ์๋จธ ์์ ๋์ผ |
| ๋ณต์ ํฉํฐ | ๋ฐ์ดํฐ ์์ ํ๊ฐ? | 3 ์ด์ |
| Flink ๋ณ๋ ฌ๋ | ๋ฆฌ์์ค ํ์ฉ ์ถฉ๋ถํ๊ฐ? | ํํฐ์ ์์ ๋์ผ |
| ์ฒดํฌํฌ์ธํธ | ์ฅ์ ๋ณต๊ตฌ ๊ฐ๋ฅํ๊ฐ? | 5-10์ด ๊ฐ๊ฒฉ |
| Watermark | ๋ฆ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋๋? | ๋ฐ์ดํฐ ํน์ฑ์ ๋ง๊ฒ |
| ์ํ ๋ฐฑ์๋ | ์ํ ํฌ๊ธฐ ์ ์ ํ๊ฐ? | ํฐ ์ํ๋ RocksDB |
| ๋คํธ์ํฌ ๋ฒํผ | ๋ฐฑํ๋ ์ ์๋๊ฐ? | ๋ฉ๋ชจ๋ฆฌ์ 10-20% |
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ฌ๊ธฐ๊น์ง ์ฝ์๋ค๋ฉด ์ ๋ง ๋๋จํด! ๐
์ฐ๋ฆฌ๋ Kafka์ Flink๋ฅผ ๊น์ด ์๊ฒ ์ดํด๋ดค์ด. ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ ์์ํด์ ์ํคํ ์ฒ, ํต์ฌ ๊ธฐ๋ฅ, ์ค์ ํ์ฉ, ์ด์ ๋ ธํ์ฐ, ๊ทธ๋ฆฌ๊ณ ๋ฏธ๋ ํธ๋ ๋๊น์ง ๋ชจ๋ ๋ค๋ค์ง.
ํต์ฌ ์์ฝ:
๐ฆ Kafka๋ ์ด๊ณ ์ ๋ฉ์์ง ์์คํ ์ด์ผ. ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฒ ์ ๋ฌํ๊ณ ๋ฒํผ๋งํ๋ ์ญํ ์ ํด. ์ด๋น ์๋ฐฑ๋ง ๊ฐ์ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ณ , ํ์ฅ์ฑ๊ณผ ๋ด๊ตฌ์ฑ์ด ๋ฐ์ด๋.
๐ Flink๋ ์ง์ง ์ค์๊ฐ ์คํธ๋ฆผ ์ฒ๋ฆฌ ์์ง์ด์ผ. ๋ณต์กํ ๊ณ์ฐ, ์ํ ๊ด๋ฆฌ, ์ด๋ฒคํธ ์๊ฐ ์ฒ๋ฆฌ๋ฅผ ์๋ฒฝํ๊ฒ ์ง์ํด. Exactly-once ๋ณด์ฅ์ผ๋ก ๋ฐ์ดํฐ ์ ํ์ฑ๋ ์๋ฒฝํด.
๐ ์ด ๋์ ํจ๊ป ์ฐ๋ฉด ์ต๊ฐ์ ์ค์๊ฐ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ์ ๋ง๋ค ์ ์์ด. ๋ทํ๋ฆญ์ค, ์ฐ๋ฒ, ๋งํฌ๋์ธ ๊ฐ์ ๊ธ๋ก๋ฒ ๊ธฐ์ ๋ค์ด ์ด๋ฏธ ์ฌ์ฉํ๊ณ ์์ง.
1. ์ง์ ํด๋ณด๊ธฐ: ๋ก์ปฌ์ ์ค์นํด์ ๊ฐ๋จํ ์์ ๋ถํฐ ์์ํด๋ด
2. ํ๋ก์ ํธ ๋ง๋ค๊ธฐ: ์ค์๊ฐ ๋ก๊ทธ ๋ถ์์ด๋ ๊ฐ๋จํ ๋ชจ๋ํฐ๋ง ์์คํ ๊ตฌ์ถ
3. ์ปค๋ฎค๋ํฐ ์ฐธ์ฌ: ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ๊ฒฝํ ๊ณต์
4. ์ ๋ฌธ๊ฐ ๋ฉํ ๋ง: ๋งํ๋ ๋ถ๋ถ์ด ์๋ค๋ฉด ์ฌ๋ฅ๋ท์์ ์ ๋ฌธ๊ฐ ๋์ ๋ฐ๊ธฐ
5. ๊ณ์ ํ์ต: ๊ธฐ์ ์ ๊ณ์ ๋ฐ์ ํ๋๊น ์ต์ ํธ๋ ๋ ํ๋ก์ฐ
์ค์๊ฐ ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐ์ ์ด์ ์ ํ์ด ์๋๋ผ ํ์์ผ. ๋ชจ๋ ์ฐ์ ์์ ์ค์๊ฐ ์ธ์ฌ์ดํธ๊ฐ ๊ฒฝ์๋ ฅ์ด ๋๊ณ ์๊ฑฐ๋ . ๊ธ์ต, ์ด์ปค๋จธ์ค, ์์ ๋ฏธ๋์ด, ์ ์กฐ, ๋ฌผ๋ฅ... ์ด๋๋ ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๊ฐ ํ์ํด.
์ฒ์์๋ ์ด๋ ต๊ฒ ๋๊ปด์ง ์ ์์ด. ์๋ก์ด ๊ฐ๋ ๋ ๋ง๊ณ , ์ค์ ํ ๊ฒ๋ ๋ง๊ณ , ๊ณ ๋ คํ ๊ฒ๋ ๋ง์ง. ํ์ง๋ง ํ๋์ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ๋ฐฐ์๊ฐ๋ค ๋ณด๋ฉด ์ด๋์ ์ ๋ฌธ๊ฐ๊ฐ ๋์ด ์์ ๊ฑฐ์ผ! ๐ช
๊ธฐ์ตํด:
โข ์๋ฒฝํ ์์คํ ์ ์์ด. ํธ๋ ์ด๋์คํ๋ฅผ ์ดํดํ๊ณ ์ํฉ์ ๋ง๊ฒ ์ ํํ๋ ๊ฒ ์ค์ํด.
โข ๋ชจ๋ํฐ๋ง๊ณผ ๋ก๊น ์ ํ์์ผ. ๋ฌธ์ ๊ฐ ์๊ฒผ์ ๋ ๋น ๋ฅด๊ฒ ํ์ ํ ์ ์์ด์ผ ํด.
โข ์๊ฒ ์์ํด์ ์ ์ง์ ์ผ๋ก ํ์ฅํด. ์ฒ์๋ถํฐ ์๋ฒฝํ๊ฒ ๋ง๋ค๋ ค๊ณ ํ์ง ๋ง.
โข ์ปค๋ฎค๋ํฐ๋ฅผ ํ์ฉํด. ํผ์ ๊ณ ๋ฏผํ์ง ๋ง๊ณ ๋ค๋ฅธ ์ฌ๋๋ค์ ๊ฒฝํ์ ๋ฐฐ์.
์, ์ด์ ๋น์ ์ฐจ๋ก์ผ! ์ด ๊ธ์์ ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ๋ฉ์ง ์ค์๊ฐ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ์ ๋ง๋ค์ด๋ด. ๊ทธ๋ฆฌ๊ณ ์ธ์ ๊ฐ ๋น์ ์ ์ฑ๊ณต ์ฌ๋ก๋ฅผ ๋ฃ๊ฒ ๋๊ธธ ๋ฐ๋ผ! ๐
์ง๋ฌธ์ด๋ ๋์์ด ํ์ํ๋ฉด ์ธ์ ๋ ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ๋ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์ ํ์ฉํด๋ด. ์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ์ ๋ฌธ๊ฐ๋ค์ด ๊ธฐ๊บผ์ด ๋์์ค ๊ฑฐ์ผ.
ํ์ด์ ๋น์ด! ํ์ดํ ! ๐๐ฏ๐ป
๐ ๋ ๊น์ด ๋ฐฐ์ฐ๊ณ ์ถ๋ค๋ฉด?
์ฌ๋ฅ๋ท์์ Kafka, Flink ์ ๋ฌธ๊ฐ๋ค์ ๋ฉํ ๋ง๊ณผ ์ปจ์คํ
์ ๋ฐ์๋ณด์ธ์!
์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ๊ฐ๋ฐ์๋ค์ด ๋น์ ์ ํ๋ก์ ํธ๋ฅผ ์ฑ๊ณต์ผ๋ก ์ด๋์ด๋๋ฆฝ๋๋ค.
www.jaenung.net
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.