Belajar Data Engineer - Streaming (Kafka/Flink)
Episode 11 of 28

Belajar Data Engineer - Streaming (Kafka/Flink)

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

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

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.

Mengapa Kafka: Log Append-Only Terdistribusi

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:

  • Topic — aliran pesan bernama dengan kategori tertentu (misal orders, payments, clicks).
  • Producer — aplikasi yang mengirim pesan ke topic.
  • Consumer — aplikasi yang membaca pesan dari topic.
100%

Partitions dan Consumer Groups

Skalabilitas Kafka lahir dari partitions: satu topic dipecah menjadi beberapa partisi, masing-masing adalah log terurut. Ini memungkinkan:

  • Parallelism: beberapa consumer membaca partisi berbeda secara paralel.
  • Ordering per key: pesan dengan key yang sama selalu masuk ke partisi yang sama — sehingga urutan per key terjaga.

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).

Jalankan Kafka via Docker
docker run -d --name kafka -p 9092:9092 apache/kafka:3.8
Buat topic dan tulis pesan (CLI kafka)
kafka-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 orders

kafka-console-producer dan kafka-console-consumer adalah cara paling cepat melihat Kafka bekerja sebelum menulis kode.

Producer dan Consumer dengan Python

Untuk produksi, kalian butuh library. Dua yang paling umum: kafka-python (sederhana) dan confluent-kafka (lebih cepat, wrapper librdkafka). Contoh dasar dengan confluent-kafka:

producer.py
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()
consumer.py
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 dan Arsitektur Kappa

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:

  1. Retention berbasis waktu/size — pesan disimpan berhari-hari/bulan, bukan dihapus setelah dibaca.
  2. Log compaction — menyimpan nilai terbaru per key, ideal untuk "state terakhir" sebuah entity.

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.

flink_window.py (agregasi window 1 menit)
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.

Praktik: Pipeline Streaming Sederhana

Rangkai konsepnya menjadi pipeline end-to-end: producer mengirim event pembayaran ke Kafka, consumer menghitung agregasi per menit, lalu menulis hasilnya ke PostgreSQL.

  1. Producer: aplikasi checkout mengirim event ke topic payments.
  2. Consumer (Python): membaca event, menghitung total per 1 menit di memori, dan menyimpan hasil ke tabel payment_rollup.
  3. Dashboard/analyst: membaca 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.

Kesalahan Umum (Common Pitfalls)

  1. Menganggap pesan langsung dihapus setelah dibaca. Kafka menyimpan pesan sesuai retention — consumer bisa dan seharusnya mengelola offsetnya sendiri.

  2. Ordering yang salah asumsi. Ordering di Kafka terjamin per partisi, bukan global. Jika urutan per key penting, pastikan key yang konsisten.

  3. Tanpa monitoring lag. Consumer yang mati diam-diam membuat pesan menumpuk. Consumer lag adalah metrik kesehatan nomor satu streaming — dibahas di episode 20.

  4. Langsung streaming tanpa kebutuhan. Kembali ke episode 2: mulai dari batch, tambahkan streaming hanya jika latency benar-benar dibutuhkan.

Penutup

Di episode 11 ini kalian telah masuk ke dunia real-time:

  • Kafka adalah distributed commit log: topics, partitions, producers, consumers, dan consumer groups.
  • Replay & retention menjadikan Kafka tulang punggung arsitektur Kappa.
  • Flink menyediakan stateful, windowed, exactly-once processing di atas stream.
  • Praktik: pipeline streaming sederhana producer → topic → consumer → rollup.

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!

Belajar Data Engineer - Streaming (Kafka/Flink) | Belajar Data Engineer