Event pesanan butuh tempat tinggal yang bisa dibaca banyak consumer secara independen dan diputar ulang kapan pun — itu pekerjaan broker Kafka. Sejak versi 4.0, Kafka hanya mengenal mode KRaft: ZooKeeper dihapus dan metadata cluster disimpan broker sendiri pada internal topic __cluster_metadata, dikunci quorum controller. Bagian ini menyiapkan dua hal: konfigurasi cluster KRaft dan topic tokokita.pesanan sebagai acuan kerja di cluster nyata, lalu topic yang sama pada simulasi lokal LocalKafka yang benar-benar kita jalankan dan uji di lingkungan ini. Konsep topic, partition, dan offset sudah dibedah pada chapter Topics, Partitions, dan Offsets; di sini kita memakainya.
Cluster KRaft dalam docker-compose #
Cara tercepat menyiapkan cluster pengembangan adalah image resmi apache/kafka dengan satu node merangkap broker dan controller. Berkas config/docker-compose.example.yaml memuat konfigurasi lengkapnya:
services:
kafka:
image: apache/kafka:4.0.0
container_name: kafka-kraft
ports:
- "9092:9092"
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0Mari kita telaah baris-baris konfigurasi di atas:
- Baris
KAFKA_NODE_ID: identitas node dalam cluster KRaft; setiap voter quorum dirujuk dengan id ini. - Baris
KAFKA_PROCESS_ROLES: broker,controller: satu proses merangkap dua peran — menyimpan data (broker) sekaligus mengelola metadata (controller). Pada cluster produksi kedua peran biasanya dipisah ke node berbeda. - Baris
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093: daftar anggota quorum metadata berformatid@host:port. Quorum inilah pengganti ZooKeeper; metadata tersimpan pada internal topic__cluster_metadatayang direplikasi antar controller. - Baris
KAFKA_LISTENERSdanKAFKA_ADVERTISED_LISTENERS: broker mendengar dua listener —PLAINTEXTdi 9092 untuk klien dari luar container (diiklankan sebagailocalhost:9092) danCONTROLLERdi 9093 khusus lalu lintas quorum. - Baris
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1: internal topic__consumer_offsetsdireplikasi sekali karena cluster ini beranggota satu node; cluster produksi memakai 3 ke atas. - Baris
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0: menghilangkan jeda awal rebalance agar eksperimen consumer group pada Bagian 6 dan 7 tidak menunggu.
Membuat Topic #
Topic pada cluster nyata dibuat lewat skrip CLI. Perintah berikut membuat topic persis seperti yang dipakai proyek (dari config/topic.yaml):
kafka-topics.sh --bootstrap-server localhost:9092 --create \
--topic tokokita.pesanan --partitions 3 --replication-factor 1 \
--config retention.ms=604800000 --config cleanup.policy=deleteTiga partition dipilih agar tiga consumer bisa membaca paralel dalam satu group pada Bagian 4, sementara retensi 7 hari (604800000 ms) memberi ruang replay histori. Nilai lengkap konfigurasi topic dan klien terdokumentasi di dokumentasi Apache Kafka
. Kutipan config/topic.yaml merangkum keputusan yang sama:
topic:
nama: tokokita.pesanan
num_partitions: 3
replication_factor: 1
konfigurasi:
retention.ms: 604800000 # 7 hari (kebijakan delete)
cleanup.policy: deleteInternal Topic dan Verifikasi Cluster #
Cluster KRaft dan consumer group menyimpan metadatanya sendiri sebagai topic internal, sehingga kafka-topics.sh --list pada cluster segar tidak pernah benar-benar kosong. Dua yang wajib dikenali: __cluster_metadata menyimpan metadata cluster (anggota quorum, daftar broker, konfigurasi) — menggantikan peran ZooKeeper sepenuhnya — dan __consumer_offsets menyimpan committed offset setiap consumer group; topic inilah yang membuat consumer group “ingat” posisinya lintas restart. Keduanya direplikasi sesuai KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR dan sebaiknya tidak diutak-atik manual.
Setelah cluster dan topic siap, tiga perintah berikut dipakai untuk memverifikasi kondisi (dijalankan pada cluster nyata, bukan di lingkungan proyek ini):
kafka-topics.sh --bootstrap-server localhost:9092 --list
kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic tokokita.pesanan
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --listPerintah --describe menampilkan jumlah partition, leader tiap partition, dan konfigurasi topic; kafka-consumer-groups.sh --list akan menampilkan group agregator-pesanan begitu consumer pertama dari Bagian 4 terhubung dan bergabung.
Topic pada Simulasi Lokal #
Simulasi LocalKafka menyimpan log tiap partition sebagai daftar di memori — append-only, offset monoton — sehingga perilaku topic, offset, dan consumer group bisa diuji tanpa broker. Membuat topic memakai metode create_topic:
from src.local_kafka import LocalKafka
broker = LocalKafka()
dibuat = broker.create_topic("tokokita.pesanan", num_partitions=3)
print(f"create_topic tokokita.pesanan (3 partition): dibuat={dibuat}")
dibuat_lagi = broker.create_topic("tokokita.pesanan", num_partitions=3)
print(f"panggilan kedua dengan partition sama : dibuat={dibuat_lagi}")
print(f"list_topics : {broker.list_topics()}")
print(f"describe : {broker.describe_topic('tokokita.pesanan')}")
print(f"partitions : {broker.partitions_for_topic('tokokita.pesanan')}")
print("statistik :")
for stat in broker.statistik_topic("tokokita.pesanan"):
print(f" {stat}")Output yang Dihasilkan:
create_topic tokokita.pesanan (3 partition): dibuat=True
panggilan kedua dengan partition sama : dibuat=False
list_topics : ['tokokita.pesanan']
describe : {'topic': 'tokokita.pesanan', 'partitions': 3, 'total_pesan': 0}
partitions : [0, 1, 2]
statistik :
{'partition': 0, 'jumlah_pesan': 0, 'offset_akhir': None}
{'partition': 1, 'jumlah_pesan': 0, 'offset_akhir': None}
{'partition': 2, 'jumlah_pesan': 0, 'offset_akhir': None}create_topic bersifat idempoten: pemanggilan kedua dengan jumlah partition sama mengembalikan False tanpa galat, meniru perilaku kafka-topics.sh --create pada topic yang sudah ada. Sebaliknya, memanggil dengan jumlah partition berbeda pada topic yang sama ditolak — mengubah jumlah partition topic yang sudah berisi data melanggar pemetaan kunci yang sudah terlanjur terjadi, sehingga simulasi memilih menolaknya lebih awal.
Dengan topic tiga partition yang siap di kedua dunia — cluster KRaft dari konfigurasi di atas dan simulasi lokal yang teruji — kita lanjut ke sisi penulis stream: producer yang menerbitkan event checkout.