Bagikan melalui


Retry Storm antipattern

Ketika layanan tidak tersedia atau sibuk, meminta klien mencoba kembali koneksi mereka terlalu sering dapat menyebabkan layanan berjuang untuk pulih, dan dapat memperburuk masalah. Juga tidak masuk akal untuk mencoba kembali selamanya, karena permintaan biasanya hanya berlaku untuk jangka waktu tertentu.

Deskripsi masalah

Di cloud, layanan terkadang mengalami masalah dan menjadi tidak tersedia untuk klien, atau harus membatasi atau membatasi klien mereka. Meskipun ini adalah praktik yang baik bagi klien untuk mencoba kembali koneksi yang gagal ke layanan, penting bagi mereka untuk tidak mencoba terlalu sering atau terlalu lama. Percobaan ulang dalam waktu singkat tidak mungkin berhasil karena layanan kemungkinan tidak akan pulih. Selain itu, layanan dapat menerima lebih banyak tekanan ketika banyak upaya koneksi yang dilakukan saat mereka mencoba untuk memulihkan, dan upaya koneksi berulang bahkan dapat membanjiri layanan dan membuat masalah yang mendasarinya lebih buruk.

Contoh berikut menggambarkan skenario saat klien terhubung ke API berbasis server. Jika permintaan tidak berhasil, klien akan segera mencoba lagi, dan terus mencoba kembali selamanya. Sering kali perilaku semacam ini lebih halus daripada dalam contoh ini, tetapi prinsip yang sama akan berlaku.

public async Task<string> GetDataFromServer()
{
    while(true)
    {
        var result = await httpClient.GetAsync(string.Format("http://{0}:8080/api/...", hostName));
        if (result.IsSuccessStatusCode) break;
    }

    // ... Process result.
}

Cara memperbaiki masalah ini

Aplikasi klien harus mengikuti beberapa praktik terbaik untuk menghindari menyebabkan banyaknya percobaan ulang.

  • Kurangi jumlah upaya coba ulang, dan jangan terus mencoba kembali dalam jangka waktu yang lama. Meskipun mungkin tampak mudah untuk menulis perulangan while(true), Anda hampir pasti tidak ingin mencoba kembali untuk jangka waktu yang lama, karena situasi yang menyebabkan permintaan dimulai mungkin telah berubah. Dalam kebanyakan aplikasi, mencoba kembali selama beberapa detik atau menit sudah cukup.
  • Jeda di antara upaya percobaan ulang. Jika layanan tidak tersedia, mencoba kembali segera tidak mungkin berhasil. Secara bertahap tingkatkan jumlah waktu Anda menunggu di antara upaya, misalnya dengan menggunakan strategi backoff eksponensial.
  • Dengan anggun menangani kesalahan. Jika layanan tidak merespons, pertimbangkan apakah masuk akal untuk membatalkan upaya dan mengembalikan kesalahan kembali ke pengguna atau penelepon komponen Anda. Pertimbangkan skenario kegagalan ini saat mendesain aplikasi Anda.
  • Pertimbangkan untuk menggunakan pola Circuit Breaker, yang dirancang khusus untuk membantu menghindari banyaknya percobaan ulang.
  • Jika server menyediakan header respons retry-after, pastikan Anda tidak melakukan percobaan ulang sampai periode waktu yang ditentukan telah berlalu.
  • Gunakan SDK resmi saat berkomunikasi dengan layanan Azure. SDK ini umumnya memiliki kebijakan dan perlindungan percobaan ulang bawaan terhadap menyebabkan atau berkontribusi pada badai percobaan ulang. Jika Anda berkomunikasi dengan layanan yang tidak memiliki SDK, atau saat SDK tidak menangani logika percobaan ulang dengan benar, pertimbangkan untuk menggunakan pustaka seperti Polly (untuk .NET) atau percobaan ulang (untuk JavaScript) untuk menangani logika coba ulang Anda dengan benar dan hindari penulisan kode sendiri.
  • Jika Anda berjalan di lingkungan yang mendukungnya, gunakan mesh layanan (atau lapisan abstraksi lain) untuk mengirim panggilan keluar. Biasanya alat ini, seperti Dapr, mendukung kebijakan percobaan ulang dan secara otomatis mengikuti praktik terbaik, seperti mundur setelah upaya berulang. Pendekatan ini berarti Anda tidak perlu menulis kode percobaan ulang sendiri.
  • Pertimbangkan permintaan batching dan gunakan pengumpulan permintaan jika tersedia. Banyak SDK menangani batching permintaan dan penyatuan koneksi atas nama Anda, yang akan mengurangi jumlah total upaya koneksi keluar yang dilakukan aplikasi, meskipun Anda masih perlu berhati-hati untuk tidak mencoba ulang koneksi ini terlalu sering.

Layanan juga harus melindungi diri dari banyaknya percobaan ulang.

  • Tambahkan lapisan gateway sehingga Anda dapat menonaktifkan koneksi selama insiden. Ini adalah contoh dari pola Bulkhead. Azure menyediakan berbagai layanan gateway untuk bermacam jenis solusi termasuk Front Door, Application Gateway, dan API Management.
  • Batasi permintaan di gateway, yang akan memastikan Anda tidak akan menerima begitu banyak permintaan sehingga komponen back-end Anda tidak dapat terus beroperasi.
  • Jika Anda melakukan pembatasan, kirim kembali header retry-after untuk membantu klien memahami kapan harus memutar ulang koneksi mereka.

Pertimbangan

  • Klien harus mempertimbangkan jenis kesalahan yang dikembalikan. Beberapa jenis kesalahan tidak menunjukkan kegagalan layanan, tetapi sebaliknya menunjukkan bahwa klien mengirim permintaan yang tidak valid. Misalnya, jika aplikasi klien menerima respons kesalahan 400 Bad Request, mencoba ulang permintaan yang sama mungkin tidak akan membantu karena server memberi tahu Anda bahwa permintaan Anda tidak valid.
  • Klien harus mempertimbangkan lamanya waktu yang cukup untuk melakukan percobaan ulang koneksi. Lamanya waktu yang harus Anda coba ulang akan didorong oleh persyaratan bisnis Anda dan apakah Anda dapat menyebarkan kesalahan secara wajar kembali ke pengguna atau penelepon. Dalam kebanyakan aplikasi, mencoba kembali selama beberapa detik atau menit sudah cukup.

Cara mendeteksi masalah

Dari perspektif klien, gejala masalah ini dapat mencakup respons atau waktu pemrosesan yang sangat panjang, beserta telemetri yang menunjukkan upaya berulang untuk mencoba ulang koneksi.

Dari perspektif layanan, gejala masalah ini dapat mencakup sejumlah besar permintaan dari satu klien dalam waktu singkat, atau sejumlah besar permintaan dari satu klien saat pulih dari gangguan. Gejala juga bisa mencakup kesulitan saat memulihkan layanan, atau kegagalan kaskade yang sedang berlangsung dari layanan tepat setelah kesalahan telah diperbaiki.

Contoh diagnosis

Bagian berikut menggambarkan satu pendekatan untuk mendeteksi potensi badai percobaan ulang, baik di sisi klien maupun sisi layanan.

Mengidentifikasi dari telemetri klien

Azure Application Insights merekam telemetri dari aplikasi dan membuat data tersedia untuk kueri dan visualisasi. Koneksi keluar dilacak sebagai dependensi, dan informasi tentang koneksi tersebut dapat diakses dan dipetakan untuk mengidentifikasi kapan klien membuat sejumlah besar permintaan keluar ke layanan yang sama.

Grafik berikut diambil dari tab Metrik dalam portal Application Insights, dan menampilkan metrik Kegagalan dependensi yang dibagi menurut Nama dependensi jarak jauh. Ini menggambarkan skenario yang berisi sejumlah besar (lebih dari 21.000) upaya koneksi yang gagal ke dependensi dalam waktu singkat.

Cuplikan layar Application Insights yang memperlihatkan kegagalan dependensi 21k ke satu dependensi dalam jangka waktu 30 menit

Mengidentifikasi dari telemetri server

Aplikasi server mungkin dapat mendeteksi sejumlah besar koneksi dari satu klien. Dalam contoh berikut, Azure Front Door bertindak sebagai gateway untuk aplikasi, dan telah dikonfigurasi untuk mencatat semua permintaan ke ruang kerja Analitik Log.

Kueri Kusto berikut dapat dijalankan terhadap Analitik Log. Kueri ini akan mengidentifikasi alamat IP klien yang telah mengirim sejumlah besar permintaan ke aplikasi dalam beberapa hari terakhir.

AzureDiagnostics
| where ResourceType == "FRONTDOORS" and Category == "FrontdoorAccessLog"
| where TimeGenerated > ago(1d)
| summarize count() by bin(TimeGenerated, 1h), clientIp_s
| order by count_ desc

Mengeksekusi kueri ini selama banyaknya percobaan ulang menunjukkan sejumlah besar upaya koneksi dari satu alamat IP.

Cuplikan layar Analitik Log yang menampilkan 81.608 koneksi masuk ke Front Door dari satu alamat IP dalam periode satu jam