Masalah yang Diselesaikan Message Broker
Ketika layanan A memanggil layanan B lewat HTTP, A ikut mati bila B mati. A juga harus menunggu B selesai — walaupun pekerjaan B tidak perlu ditunggu, misalnya mengirim email atau membuat invoice.
Message broker memutus keterikatan itu: A menaruh pesan dan langsung lanjut; B mengambilnya kapan pun ia siap. Bila B mati satu jam, pesan menunggu dengan sabar.
- Decoupling — pengirim tidak perlu tahu siapa yang memproses, atau berapa banyak.
- Buffering — lonjakan trafik ditampung antrean, bukan menghantam basis data.
- Ketahanan — konsumer boleh mati, restart, atau di-deploy tanpa kehilangan pekerjaan.
Kesalahpahaman Nomor Satu
Producer tidak pernah mengirim pesan ke queue. Producer mengirim ke exchange. Exchange-lah yang memutuskan queue mana yang menerima salinannya — atau tidak sama sekali.
Producer Broker Consumer
│ │ │
│ publish │ │
│ exchange="pesanan" │ │
│ routing_key= │ │
│ "pesanan.dibuat" │ │
├──────────────────────>│ │
│ ┌────▼─────┐ │
│ │ EXCHANGE │ cocokkan routing key │
│ │ pesanan │ dengan binding │
│ └────┬─────┘ │
│ ┌──────────┼──────────┐ │
│ │ │ │ │
│ binding: binding: binding: │
│ "pesanan.*" "pesanan. "pembayaran.*" │
│ │ dibuat" │ (tidak cocok) │
│ ┌───▼───┐ ┌───▼───┐ ✗ │
│ │ QUEUE │ │ QUEUE │ │
│ │ audit │ │ kirim │──────── deliver ─────────>│
│ └───────┘ └───────┘ │
│ │
│ Satu pesan → DUA queue → masing-masing satu salinan │Empat Komponen Inti
| Komponen | Tugas | Analogi pos |
|---|---|---|
| Exchange | Menerima pesan, memutuskan tujuannya | Kantor sortir |
| Queue | Menyimpan pesan sampai diambil | Kotak pos penerima |
| Binding | Aturan penghubung exchange → queue | Daftar alamat di kantor sortir |
| Routing key | Label pada pesan, dicocokkan dengan binding | Alamat di amplop |
Bila tidak ada satu pun binding yang cocok, exchange membuang pesan tanpa error apa pun. Publish tetap dilaporkan berhasil.
Ini penyebab nomor satu dari "pesan saya hilang". Dua pertahanannya:
1. mandatory=true + basic.return → broker mengembalikan pesan tak terkirim
2. alternate-exchange → pesan tak terkirim dibuang ke exchange
cadangan, bukan ke ketiadaanConnection dan Channel
Satu connection adalah satu koneksi TCP — mahal untuk dibuat. Di dalamnya ada banyak channel, yaitu sesi ringan yang menjadi tempat sebenarnya semua operasi AMQP dijalankan.
Aplikasi
└── Connection (1 TCP) ← buat SEKALI, pakai selamanya
├── Channel 1 → publish
├── Channel 2 → consume queue A
└── Channel 3 → consume queue B
❌ satu connection per pesan → broker kehabisan file descriptor
✅ satu connection per proses, satu channel per thread/goroutineChannel bukan objek thread-safe. Dua thread yang menulis ke channel yang sama akan merusak protokol dan memutus channel dengan error UNEXPECTED_FRAME. Aturannya: satu channel untuk satu thread.
Virtual Host
vhost adalah namespace penuh: exchange, queue, binding, dan izin di dalamnya terisolasi total. Dua tim bisa memakai broker yang sama dengan nama queue identik tanpa bertabrakan. Vhost default bernama /.
Kapan RabbitMQ, Kapan Kafka
| RabbitMQ | Kafka | |
|---|---|---|
| Model | Antrean: pesan dikonsumsi lalu hilang | Log: pesan bertahan, konsumer punya offset |
| Routing | Sangat kaya (topic, headers, DLX) | Sederhana (topic + partisi) |
| Per-pesan | Ack, retry, prioritas, TTL per pesan | Per-batch, per-partisi |
| Throughput puncak | Puluhan ribu pesan/detik | Jutaan pesan/detik |
| Cocok untuk | Task queue, RPC, routing kompleks | Event streaming, replay, analitik |
Pertanyaan pemilih yang praktis: apakah pesan yang sudah diproses masih perlu dibaca ulang oleh konsumer lain nanti? Bila ya, itu wilayah Kafka atau RabbitMQ Stream. Bila tidak, antrean klasik RabbitMQ lebih sederhana dan lebih tepat.