1. Kenapa Monolith Tidak Butuh Ini, Microservices Butuh
Di monolith, memanggil fungsi lain adalah pemanggilan di dalam memori: selalu berhasil, selalu instan. Di microservices, setiap panggilan melewati jaringan yang bisa lambat, putus, atau menjawab separuh jalan.
Layanan Pembayaran melambat menjadi 30 detik per permintaan. Layanan Pesanan yang memanggilnya ikut menunggu, thread pool-nya habis. Layanan Keranjang yang memanggil Pesanan ikut menunggu, thread-nya juga habis. Dalam hitungan menit, seluruh sistem mati padahal hanya satu layanan yang bermasalah. Inilah pola kegagalan paling umum dan paling merusak di arsitektur microservices.
2. Timeout โ Pertahanan Paling Dasar
Banyak pustaka HTTP secara default menunggu tanpa batas. Satu dependensi yang menggantung berarti thread Anda menggantung selamanya. Setiap panggilan keluar wajib punya timeout eksplisit โ tanpa kecuali.
Klien browser : 10 detik
โ API Gateway : 9 detik
โ Layanan Pesanan : 6 detik
โ Layanan Stok : 2 detik
โ Layanan Harga : 2 detik
Aturannya: timeout di hulu HARUS lebih besar daripada
total timeout seluruh panggilan di hilirnya.
Yang lebih baik daripada timeout tetap adalah deadline propagation: kirimkan sisa waktu yang tersedia bersama permintaan. Bila layanan hulu sudah menunggu 5 detik dari anggaran 6 detik, layanan hilir tahu ia hanya punya 1 detik dan bisa langsung menyerah alih-alih mengerjakan pekerjaan yang hasilnya pasti dibuang. Ini bawaan di gRPC lewat context.WithTimeout, dan bisa ditiru di HTTP lewat header seperti X-Request-Deadline.
3. Retry โ dan Cara Melakukannya Tanpa Merusak
for (let i = 0; i < 3; i++) {
try { return await panggilLayanan(); }
catch (e) { /* langsung coba lagi */ }
}
// 1.000 klien gagal -> 3.000 permintaan menghantam
// layanan yang SEDANG kepayahan. Ini disebut retry storm.
async function panggilDenganRetry(fn, maksPercobaan = 3) {
let jedaDasar = 100; // ms
for (let percobaan = 0; percobaan < maksPercobaan; percobaan++) {
try {
return await fn();
} catch (err) {
if (!bolehDiulang(err) || percobaan === maksPercobaan - 1) throw err;
// Backoff eksponensial: 100ms, 200ms, 400ms, ...
const jeda = jedaDasar * Math.pow(2, percobaan);
// Jitter: acak PENUH agar klien tidak serempak
const jedaAcak = Math.random() * jeda;
await tidur(jedaAcak);
}
}
}
function bolehDiulang(err) {
// JANGAN ulangi 4xx -- permintaannya memang salah,
// diulang seribu kali pun tetap salah.
if (err.status >= 400 && err.status < 500 && err.status !== 429) return false;
return true; // 5xx, timeout, kegagalan jaringan
}
1. Hanya ulangi operasi yang idempoten. Mengulang GET aman. Mengulang "buat pesanan" bisa menghasilkan pesanan ganda kecuali Anda memakai kunci idempotensi.
2. Jangan retry berlapis. Bila gateway mengulang 3 kali, dan tiap layanan di bawahnya juga mengulang 3 kali, satu permintaan pengguna bisa berubah menjadi 27 permintaan ke layanan terdalam. Tetapkan retry di satu lapisan saja, biasanya yang paling dekat dengan dependensi.
4. Circuit Breaker
Bila sebuah layanan jelas-jelas sedang mati, terus-menerus memanggilnya hanya membuang waktu dan menghabiskan thread. Circuit breaker berhenti mencoba untuk sementara.
gagal melewati ambang
CLOSED โโโโโโโโโโโโโโโโโโโโโโโ> OPEN
โ โ
โ percobaan berhasil โ setelah jeda pemulihan
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโ HALF-OPEN
percobaan gagal โโโโโโโ (kembali ke OPEN)
| Keadaan | Perilaku |
|---|---|
| CLOSED | Normal. Semua panggilan diteruskan, kegagalan dihitung. |
| OPEN | Semua panggilan langsung ditolak tanpa menyentuh jaringan. Cepat gagal, sumber daya terjaga. |
| HALF-OPEN | Beberapa panggilan percobaan diizinkan lewat untuk menguji apakah layanan sudah pulih. |
ambangKegagalan : 50% (persentase, bukan jumlah absolut) jumlahPanggilanMinimum: 20 (jangan buka circuit karena 1 dari 2 gagal) jendelaPengamatan : 60 detik jedaSebelumHalfOpen : 30 detik panggilanPercobaan : 3
Menolak permintaan dengan cepat memang melindungi sistem Anda, tetapi pengguna tetap melihat error. Pasangkan selalu dengan fallback yang bermakna: tampilkan data dari cache meski agak basi, tampilkan rekomendasi umum alih-alih personal, atau terima pesanan lalu proses pembayarannya secara asinkron. Degradasi anggun jauh lebih baik daripada halaman error.
5. Bulkhead โ Isolasi Sumber Daya
Nama ini diambil dari sekat kedap air di lambung kapal: satu kompartemen bocor tidak menenggelamkan seluruh kapal.
TANPA bulkhead -- satu kolam untuk semua: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ Thread pool: 100 โ โ Layanan Rekomendasi melambat โ โ -> 100 thread habis menunggu โ โ -> Checkout ikut mati โ โ padahal checkout tak terkait โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ DENGAN bulkhead -- kolam terpisah: โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ โ Pembayaran โ Rekomendasi โ Pencarian โ โ 40 thread โ 20 thread โ 40 thread โ โ โ sehat โ โ habis โ โ sehat โ โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโ Fitur rekomendasi mati, checkout tetap jalan.
Bila laporan analitik dan transaksi checkout memakai kolam koneksi yang sama, satu kueri laporan yang berat bisa menghabiskan seluruh koneksi dan melumpuhkan checkout. Pisahkan kolam koneksinya โ atau lebih baik lagi, pisahkan ke replika baca tersendiri.
6. Rate Limiting & Load Shedding
# Rate limiting: membatasi berapa banyak yang boleh masuk
# - Token bucket : mengizinkan lonjakan sesaat, cocok untuk API publik
# - Sliding window: lebih ketat dan akurat
# - Balas 429 Too Many Requests + header Retry-After
# Load shedding: membuang permintaan saat sistem KEPAYAHAN
if (panjangAntrian > ambangKritis) {
return balas503("Sedang sibuk, coba beberapa saat lagi");
}
Load shedding terasa kejam, tetapi sistem yang menerima semua permintaan saat kelebihan beban akan melambat untuk semua orang sampai akhirnya mati total. Menolak sebagian permintaan lebih awal menjaga sisanya tetap terlayani dengan latensi normal.
7. Menguji Ketahanan: Chaos Engineering
Pola-pola di atas hanya berguna bila benar-benar berfungsi saat dibutuhkan. Satu-satunya cara memastikannya adalah sengaja membuat kegagalan.
- Suntikkan latensi โ tambahkan 5 detik pada satu dependensi, lihat apakah timeout Anda bekerja.
- Matikan satu instance โ apakah load balancer mengalihkan trafik dengan benar?
- Putuskan jaringan antara dua layanan โ apakah circuit breaker terbuka?
- Habiskan CPU atau memori pada satu node โ apakah probe kesehatan mendeteksinya?
Chaos engineering bukan merusak secara acak. Tuliskan dulu hipotesisnya: "bila Layanan Rekomendasi mati, halaman produk tetap tampil dalam 2 detik tanpa bagian rekomendasi". Jalankan eksperimennya. Bila hipotesis salah, Anda baru saja menemukan bug produksi sebelum pengguna menemukannya.