Memasuki dunia real-time dengan Apache Kafka: konsep topics, producers, consumers, dan consumer groups, koneksi ke arsitektur Kappa, peran Flink untuk stateful processing, serta praktik membangun pipeline streaming sederhana dari nol

Di episode 10 kalian memproses data dalam batch — data yang "duduk diam" lalu diproses terjadwal. Tapi sebagian data tidak bisa menunggu: event pembayaran, klik pengguna, log server, telemetri IoT. Data ini lahir, dan dalam hitungan detik harus sudah memengaruhi keputusan. Inilah dunia streaming.
Episode ini memperkenalkan Apache Kafka — broker event standar industri — dan Apache Flink untuk pemrosesan stream stateful. Kalian akan memahami konsep intinya, menghubungkannya dengan arsitektur Kappa dari episode 2, dan menjalankan pipeline streaming pertama.
Kafka bukan "message queue" biasa. Ia adalah distributed commit log: setiap pesan ditambahkan (append) ke ujung log dan dibaca dari posisi yang diinginkan — bukan dihapus setelah dibaca. Ini yang membuat Kafka bisa replay: konsumen bisa membaca ulang data dari awal kapan pun, tanpa melibatkan produsen.
Tiga konsep inti yang wajib dikuasai:
orders, payments, clicks).Skalabilitas Kafka lahir dari partitions: satu topic dipecah menjadi beberapa partisi, masing-masing adalah log terurut. Ini memungkinkan:
Consumer group adalah sekumpulan consumer yang berbagi beban: tiap partisi dibaca oleh tepat satu consumer dalam satu group. Jika satu consumer mati, partisinya dialihkan ke consumer lain (rebalance).
docker run -d --name kafka -p 9092:9092 apache/kafka:3.8kafka-topics --bootstrap-server localhost:9092 \
--create --topic orders --partitions 3 --replication-factor 1
echo '{"order_id":1,"amount":25000}' | \
kafka-console-producer --bootstrap-server localhost:9092 --topic orderskafka-console-producer dan kafka-console-consumer adalah cara paling cepat melihat Kafka bekerja sebelum menulis kode.
Untuk produksi, kalian butuh library. Dua yang paling umum: kafka-python (sederhana) dan confluent-kafka (lebih cepat, wrapper librdkafka). Contoh dasar dengan confluent-kafka:
from confluent_kafka import Producer
import json
p = Producer({"bootstrap.servers": "localhost:9092"})
def delivery_callback(err, msg):
if err:
print(f"Gagal kirim: {err}")
else:
print(f"OK: pesan ke {msg.topic()}[{msg.partition()}] offset {msg.offset()}")
for i in range(10):
p.produce(
"orders",
key=str(i % 3), # key → partisi konsisten
value=json.dumps({"order_id": i, "amount": 25000}),
callback=delivery_callback,
)
p.flush()from confluent_kafka import Consumer, KafkaError
import json
c = Consumer({
"bootstrap.servers": "localhost:9092",
"group.id": "orders-etl", # nama consumer group
"auto.offset.reset": "earliest",
})
c.subscribe(["orders"])
while True:
msg = c.poll(1.0)
if msg is None:
continue
if msg.error():
if msg.error().code() != KafkaError._PARTITION_EOF:
print(f"Error: {msg.error()}")
continue
data = json.loads(msg.value())
print(f"order {data['order_id']}: amount {data['amount']}")Perhatikan perbedaan kunci dari queue biasa: consumer menentukan posisinya sendiri (auto.offset.reset: earliest berarti baca dari awal jika belum ada offset) dan bisa replay — kekuatan khas Kafka.
Kafka adalah tulang punggung Kappa architecture dari episode 2: semua data dianggap sebagai stream, dan data historis hanyalah stream yang diputar ulang dari awal. Ini bekerja karena dua fitur Kafka:
Dengan keduanya, satu pipeline streaming bisa menangani baik data baru (append) maupun rekonstruksi state historis (replay) — tidak perlu dua codebase seperti Lambda.
Kafka menangani transport; Flink menangani komputasi di atas stream. Perbedaan kuncinya: Flink bisa memproses stateful dan windowed — agregasi bergulir, event-time processing, exactly-once — yang tidak bisa dilakukan consumer Kafka biasa.
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.datastream.connectors import FlinkKafkaConsumer
from pyflink.common.serialization import SimpleStringSchema
from pyflink.table import StreamTableEnvironment
env = StreamExecutionEnvironment.get_execution_environment()
t_env = StreamTableEnvironment.create(env)
t_env.execute_sql("""
CREATE TABLE orders (
order_id INT,
amount DECIMAL(12,2),
event_time TIMESTAMP(3),
WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
'connector' = 'kafka',
'topic' = 'orders',
'properties.bootstrap.servers' = 'localhost:9092',
'format' = 'json',
'scan.startup.mode' = 'earliest-offset'
)
""")
t_env.execute_sql("""
SELECT TUMBLE_START(event_time, INTERVAL '1' MINUTE) AS window_start,
COUNT(order_id) AS orders_per_minute,
SUM(amount) AS revenue_per_minute
FROM orders
GROUP BY TUMBLE(event_time, INTERVAL '1' MINUTE)
""").print()Perhatikan WATERMARK — mekanisme Flink untuk menangani data yang datang terlambat. Streaming bukan sekadar "proses cepat", melainkan pengelolaan waktu yang teliti.
Tip
Aturan pemilihan yang praktis: Kafka + consumer biasa jika kalian hanya memindahkan event (ingestion ke lake). Flink jika kalian butuh agregasi window, join stream, atau exactly-once semantics. Jangan langsung menambah Flink untuk sekadar menyalin pesan.
Rangkai konsepnya menjadi pipeline end-to-end: producer mengirim event pembayaran ke Kafka, consumer menghitung agregasi per menit, lalu menulis hasilnya ke PostgreSQL.
payments.payment_rollup.payment_rollup untuk monitoring real-time.Pipeline ini sederhana tapi memuat seluruh konsep penting: topic, producer, consumer group, dan rollup window. Untuk skala produksi, langkah 2 digantikan Flink atau Spark Structured Streaming — prinsipnya sama.
Menganggap pesan langsung dihapus setelah dibaca. Kafka menyimpan pesan sesuai retention — consumer bisa dan seharusnya mengelola offsetnya sendiri.
Ordering yang salah asumsi. Ordering di Kafka terjamin per partisi, bukan global. Jika urutan per key penting, pastikan key yang konsisten.
Tanpa monitoring lag. Consumer yang mati diam-diam membuat pesan menumpuk. Consumer lag adalah metrik kesehatan nomor satu streaming — dibahas di episode 20.
Langsung streaming tanpa kebutuhan. Kembali ke episode 2: mulai dari batch, tambahkan streaming hanya jika latency benar-benar dibutuhkan.
Di episode 11 ini kalian telah masuk ke dunia real-time:
Di episode 12 selanjutnya kita membahas cara data masuk ke sistem secara efisien: data ingestion & CDC — full vs incremental load, Change Data Capture dengan Debezium, dan praktik CDC dari database ke warehouse. Sampai jumpa di episode 12!