Java Modern untuk Produksi
EN
Materi 7 dari 9 · 15m

Timeout, retry, dan tidak memperbesar gangguan

Dua timeout alih-alih satu, tiga aturan yang membuat retry aman, kenapa jitter lebih penting daripada backoff, dan apa arti urutan aspect Resilience4j sebenarnya.

Semua sejauh ini soal kode yang enak dibaca. Bagian ini soal kode yang masih menjawab ketika sesuatu yang dia andalkan tidak.

Setiap panggilan remote punya timeout, dan kamu yang memilihnya

Panggilan tanpa timeout mewarisi timeout dari lapisan yang belum kamu baca — default HTTP client, setelan socket, kadang tidak ada sama sekali. Saat beban tinggi, itu bedanya antara “dependensi lambat” dan “thread pool penuh menunggu dependensi itu”. Kegagalannya tidak tinggal di tempat asalnya: service-mu berhenti menjawab request yang sama sekali tidak berhubungan dengan dependensi yang lambat itu.

Dua angka, bukan satu:

Yang dibatasi
connect timeout mendapatkan koneksi — gagal cepat kalau host-nya hilang
read/response timeout menunggu jawaban setelah tersambung

Keduanya gagal karena alasan berbeda dan layak dapat nilai berbeda. Connect timeout 30 detik hampir selalu salah; kalau TCP handshake belum selesai dalam satu atau dua detik, dia tidak akan selesai.

Dan timeout-nya harus lebih pendek dari kesabaran si pemanggil. Rantai di mana setiap hop mengizinkan 30 detik berarti browser-nya sudah menyerah jauh sebelum panggilan terdalam menyerah, dan semua thread di antaranya masih memegang pekerjaan yang tidak diinginkan siapa pun.

Retry memperbesar gangguan

Retry adalah permintaan kedua ke layanan yang baru saja gagal. Tiga retry di empat instance berarti enam belas permintaan tiba di sesuatu yang sudah kepayahan — dan begitulah degradasi sebagian menjadi gangguan total.

Tiga aturan membuat retry aman:

Hanya untuk error yang masuk akal bisa berhasil di percobaan berikutnya. Timeout, connection reset, 503: ya. 400 atau 422: jangan pernah — request-nya salah dan akan salah lagi. Me-retry kegagalan validasi adalah beban murni dengan hasil yang sudah dijamin.

Jangan pernah untuk write non-idempoten tanpa idempotency key. “Buat pembayaran” yang di-retry setelah timeout sangat mungkin membuat dua pembayaran, karena timeout tidak memberi tahu apa pun soal apakah sisi lain sudah memprosesnya. Key-nya memungkinkan server mengenali pengulangan dan mengembalikan hasil yang pertama.

Dengan backoff dan jitter. Retry berjeda tetap akan serempak: setiap klien yang gagal di saat yang sama akan kembali di saat yang sama, dan bebannya datang bergelombang yang justru menahan dependensinya tetap tumbang. Exponential backoff melebarkan gelombangnya; jitter — mengacak setiap jeda — itulah yang memutus keserempakannya, dan dia lebih penting daripada eksponennya. Tanpa itu, seribu klien yang backoff dengan pola identik tetap tiba bersamaan, hanya lebih lambat.

Urutan aspect bukan kosmetik

Urutan default Resilience4j membungkus Retry di luar CircuitBreaker:

Retry ( CircuitBreaker ( TimeLimiter ( Bulkhead ( call ) ) ) )

Baca dari dalam ke luar dan konsekuensinya konkret: setiap percobaan retry adalah panggilan terpisah yang melewati breaker, jadi percobaan yang gagal ikut dihitung untuk membukanya — dan begitu breaker terbuka, retry yang tersisa gagal seketika tanpa menyentuh jaringan. Itu biasanya yang kamu mau: retry berhenti menjadi beban begitu breaker memutuskan dependensinya sedang tidak sehat.

Balik urutannya — breaker di luar retry — dan satu panggilan logis menjadi satu peristiwa breaker berapa pun percobaannya, sehingga breaker hanya melihat sepertiga kegagalannya dan butuh tiga kali lebih lama untuk terbuka.

Gagal cepat itu fitur

Naluri saat dependensi melambat adalah menunggu lebih lama. Itu terbalik. Setiap request yang menunggu adalah satu thread, satu koneksi, dan satu slot antrean yang bisa dipakai bagian sistem yang sehat. Kegagalan cepat dengan jawaban yang menurun kualitasnya — nilai dari cache, halaman sebagian, atau “coba lagi sebentar” yang eksplisit — menjaga sisa service tetap bisa dipakai.

Itu juga sebabnya memperbesar connection pool untuk memperbaiki latensi biasanya memperburuk: lebih banyak query bersamaan ke database yang sudah jadi bottleneck berarti lebih banyak contention, query lebih lambat, dan sekarang pool-nya ikut habis.


Konfigurasinya dalam kode, dengan nilai yang sudah lewat insiden produksi: Referensi Pola Resilience4j.