L
Event-Driven Architecture ๐ŸฅŠ Head-to-Head Architectural Battle โ€ข Literally Web ID

Merancang Sistem Event-Driven Skala Masif Menggunakan Kafka dan Go

โฑ๏ธ 14 menit baca โ€ข ๐Ÿ“… 20 Oct 2026 โ€ข ๐Ÿ‘๏ธ 2 views
Merancang Sistem Event-Driven Skala Masif Menggunakan Kafka dan Go

Pukul dua pagi, layar monitor saya menyala terang menampilkan grafik Grafana yang mendadak menukik tajam ke zona merah. Tim backend baru saja merilis fitur notifikasi massal, dan sistem event-driven berbasis RabbitMQ yang kamiandalkan langsung mengalami bottleneck. Pesan menumpuk di antrean, memori server melonjak, dan koneksi TCP mulai berguguran. Di ruang perang virtual, perdebatan sengit langsung pecah antara kubu yang ingin memodernisasi infrastruktur ke Apache Kafka dengan produsen Go, melawan mereka yang masih bertahan dengan arsitektur tradisional. Ini bukan sekadar diskusi teoretis di atas kertas; ini adalah pertarungan hidup mati demi menyelamatkan performa jutaan request per detik yang melintasi ekosistem web modern kami, mulai dari frontend Nginx, backend Go, hingga penyimpanan S3 dan Redis.

Mari kita bedah langsung performa kedua pendekatan dalam kepala-ke-kepala arsitektural ini melalui matriks perbandingan performa di lapangan:

Metrik Penilaian Apache Kafka (Event Streaming) RabbitMQ (Traditional Broker)
Kecepatan Tulis (Throughput) 10/10 (Jutaan pesan/detik) 6/10 (Ratusan ribu pesan/detik)
Latency (P99) 9/10 (Konsisten rendah, < 5ms) 7/10 (Bisa melonjak saat backpressure)
Penggunaan RAM 8/10 (Sangat efisien, berbasis OS PageCache) 5/10 (Boros RAM jika antrean menumpuk)
Kompleksitas Setup 6/10 (Butuh ZooKeeper/Kraft & Tuning ketat) 9/10 (Plug and play, sangat ramah pemula)
Ekosistem & Integrasi 9/10 (Standar industri untuk Big Data) 8/10 (Unggul di AMQP & routing kompleks)

1. Pertarungan Filosofi: Dua Cara Pandang Berbeda dalam Memecahkan Masalah

Ketika kita berbicara tentang pengiriman pesan asinkron, bayangkan Anda sedang mengelola sistem logistik raksasa. Pendekatan tradisional seperti menggunakan antrean berbasis broker konvensional memperlakukan pesan seperti surat kilat: begitu surat dibaca oleh kurir, surat tersebut dibuang dari kotak pos. Filosofinya adalah task queueโ€”pekerjaan harus diselesaikan dan dihapus.

Di sisi lain, Apache Kafka melihat dunia dari sudut pandang yang berbeda: sebuah jurnal transaksi abadi (immutable log). Pesan tidak pernah dihapus begitu saja hanya karena sudah dibaca. Pesan dicatat secara berurutan ke dalam disk dan dibiarkan di sana sampai masa retensinya habis. Artinya, banyak konsumen berbeda bisa membaca data yang sama berulang kali tanpa mengganggu satu sama lain. Ketika dipadukan dengan performa konkurensi Golang yang luar biasa, filosofi ini mengubah cara kita mendesain ketahanan sistem secara drastis.

[!IMPORTANT] Jangan pernah menyamakan Kafka dengan message queue biasa. Kafka adalah platform event streaming terdistribusi. Kesalahan memahami filosofi ini sering kali berujung pada desain skema partisi yang buruk dan kebocoran memori pada consumer.

Transisi dari pola pikir task dispatching ke event streaming inilah yang memaksa kami merombak total cara backend Go berinteraksi dengan infrastruktur, membawa kita langsung ke arena pengujian ergonomi kode sehari-hari.

2. Ronde 1: Ergonomi Menulis Kode & Pengalaman Konfigurasi Sehari-hari

Menulis kode penghubung antrean di Go menggunakan pustaka modern seperti segmentio/kafka-go atau confluentinc/confluent-kafka-go memberikan kepuasan tersendiri bagi seorang engineer. Tidak ada lagi kerumitan callback hell atau manajemen koneksi manual yang melelahkan seperti yang sering kita temui di ekosistem bahasa lain.

Mari kita lihat bagaimana elegan dan tangguhnya implementasi producer berkinerja tinggi di Go untuk mengirimkan event web scale:

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/segmentio/kafka-go"
)

func main() {
	// Inisialisasi Kafka Writer dengan konfigurasi high-throughput
	// Menggunakan async write dan batching untuk performa maksimal
	writer := &kafka.Writer{
		Addr:     kafka.TCP("localhost:9092"),
		Topic:    "user-activity-events",
		Balancer: &kafka.LeastBytes{},
		BatchSize: 100,
		BatchTimeout: 10 * time.Millisecond,
		RequiredAcks: kafka.RequireOne, // Keseimbangan antara kecepatan dan durability
	}
	defer writer.Close()

	ctx := context.Background()
	err := writer.WriteMessages(ctx,
		kafka.Message{
			Key:   []byte("user-1001"),
			Value: []byte(`{"action":"login","ip":"192.168.1.50","timestamp":1698765432}`),
		},
	)

	if err != nil {
		log.Fatalf("Gagal mengirim event ke Kafka: %v", err)
	}

	fmt.Println("Event berhasil dipublish dengan latensi minimal!")
}

Konfigurasi di atas dirancang agar tidak membebani jaringan secara berlebihan. Dengan memanfaatkan batching, Go mengumpulkan sekian banyak event di memori sebelum mengirimkannya sekaligus ke broker Kafka. Namun, kemudahan di sisi kode ini harus dibayar dengan ketelitian ekstra saat melakukan tuning konfigurasi server Kafka itu sendiri, terutama pada bagian partisi.

3. Ronde 2: Uji Ketahanan di Bawah Gempuran Beban Ekstrem

Saat kami melakukan pengujian beban menggunakan k6 dan skenario trafik tiruan setara 50.000 request HTTP per detik yang masuk melalui Nginx, performa Kafka mulai menunjukkan taringnya yang sesungguhnya. Di sinilah desain partisi menjadi penentu kemenangan.

Jika Anda membuat topik Kafka hanya dengan 1 partisi, Anda sedang menciptakan kemacetan lalu lintas tol berjalur tunggal. Seluruh beban pembacaan akan menumpuk pada satu consumer saja, membuat CPU melonjak hingga 100%. Kuncinya adalah mendistribusikan kunci (partition key) secara merata berdasarkan hash ID pengguna atau UUID transaksi.

Berikut adalah ilustrasi bagaimana data didistribusikan ke dalam multi-partisi untuk menjamin parallelism di sisi Go:

[Producer Go] ---> (Partition Key: user-1001) ---> [Kafka Broker: Topic 'events']
                                                      โ”œโ”€โ”€ Partition 0 ---> [Consumer Go A]
                                                      โ”œโ”€โ”€ Partition 1 ---> [Consumer Go B]
                                                      โ””โ”€โ”€ Partition 2 ---> [Consumer Go C]

Dengan memecah topik menjadi beberapa partisi, consumer group di Go dapat berjalan secara konkuren menggunakan goroutine tanpa saling berebut pesan. Hasil uji beban menunjukkan bahwa latensi P99 tetap stabil di bawah 4 milidetik, bahkan ketika jutaan pesan mengalir deras tanpa henti ke dalam kluster.

4. Ronde 3: Konsumsi Sumber Daya Server & Efisiensi Biaya Nyata

Salah satu mitos terbesar dalam dunia infrastruktur adalah anggapan bahwa Kafka itu boros sumber daya. Faktanya, jika dikonfigurasi dengan benar, Kafka justru jauh lebih ramah terhadap RAM dibandingkan message broker tradisional yang gemar menumpuk seluruh antrean di dalam memori utama.

Kafka mendelegasikan manajemen penyimpanan data ke sistem operasi melalui PageCache Linux. Alih-alih membaca dan menulis langsung dari memori JVM secara masif, Kafka membaca langsung dari cache kernel. Ini membuat konsumsi RAM pada server berkapasitas 32GB tetap stabil di kisaran 30-40%, sementara sisa kapasitas memori digunakan secara optimal oleh sistem operasi.

Di sisi lain, aplikasi Go yang bertindak sebagai consumer berjalan sangat ringan. Sebuah binary Go tunggal yang mengelola consumer group hanya memakan memori sekitar 20MB hingga 50MB, jauh lebih efisien dibandingkan runtime berbasis bahasa lain yang membutuhkan ratusan megabyte hanya untuk garbage collection dasarnya. Efisiensi ini langsung berdampak positif pada pemangkasan biaya infrastruktur awan (AWS/GCP) secara signifikan.

[!TIP] Selalu atur parameter vm.swappiness = 1 pada kernel Linux server Kafka Anda. Ini memastikan sistem operasi memaksimalkan PageCache untuk penyimpanan pesan dan mencegah kernel melakukan swap ke disk yang dapat merusak latensi.

5. Kapan Wajib Menggunakan Apache Kafka

Anda wajib memilih Apache Kafka ketika proyek Anda menghadapi karakteristik berikut:

  1. Skala Data Masif: Volume event harian melampaui puluhan juta hingga miliaran pesan.
  2. Kebutuhan Replay Data: Ada skenario di mana sistem analitik atau machine learning Anda perlu membaca ulang data historis dari beberapa hari atau minggu lalu tanpa merusak data yang ada.
  3. Arsitektur Microservices Kompleks: Berbagai layanan independen (seperti sistem billing, notifikasi, audit log, dan search engine) perlu mendengarkan aliran data yang sama secara serentak dan mandiri.
  4. Jaminan Urutan Pesan (Order Guarantee): Anda membutuhkan kepastian mutlak bahwa urutan event per entitas (misalnya status pesanan e-commerce) diproses secara berurutan tanpa ada yang terlewat.

6. Kapan Wajib Menggunakan Alternatif Lain (RabbitMQ)

Namun demikian, Kafka bukanlah peluru perak untuk segala masalah. Jangan gunakan Kafka jika:

  1. Aplikasi Anda Skala Kecil hingga Menengah: Jika traffic Anda hanya ribuan event per hari, kompleksitas mengelola kluster Kafka (bahkan dengan mode KRaft) adalah pemborosan tenaga kerja.
  2. Kompleksitas Routing Pesan Tinggi: Jika Anda membutuhkan fitur topic exchanges, header routing, atau dead-letter berbasis pola pertukaran yang rumit secara dinamis, RabbitMQ jauh lebih intuitif dan mudah diatur.
  3. Task Queue Sederhana: Ketika fokus Anda murni hanya membagi pekerjaan latar belakang (seperti konversi video atau pengiriman email tunggal) di mana pesan langsung dihapus setelah selesai dikerjakan.

7. Keputusan Akhir: Rekomendasi Jujur untuk Tim Engineering

Kembali ke ruang perang kami pada pukul dua pagi kala itu: setelah kami menerapkan arsitektur Kafka dengan partisi yang tepat dan menyandingkannya dengan consumer berbasis Go yang tangguh, grafik Grafana langsung berubah drastis menjadi garis hijau yang tenang. Beban CPU turun drastis, latensi menghilang, dan sistem kembali bernapas lega.

Keputusan akhirnya jelas: jika Anda sedang membangun fondasi produk masa depan yang berorientasi pada skala masif, integrasi data real-time, dan ketahanan jangka panjang, kombinasi Apache Kafka dan Golang adalah investasi arsitektural terbaik yang bisa Anda ambil. Jangan takut dengan kurva belajarnya yang curam; begitu sistem ini berjalan stabil di jalur produksi, Anda tidak akan pernah ingin menoleh ke belakang lagi.

๐Ÿท๏ธ Tags: #Golang #Backend Architecture #High-Throughput #Event-Driven #Kafka