Kesalahan 500, 502, 503, dan 504 dari API: apa artinya dan cara menanganinya

Kode status dari 500 hingga 599 berarti bahwa server gagal memenuhi permintaan yang terlihat valid. Permintaan Anda bukan masalah, jadi mengirimnya lagi mungkin berhasil. Apakah Anda harus mengirimnya lagi tergantung pada kode status dan pada apa yang dilakukan permintaan. Untuk definisinya, lihat RFC 9110, bagian 15.6.

Arti setiap kode status

Kode status Apa artinya Coba lagi?
500 Internal Server Error Server mengalami kondisi yang tidak diharapkan Ini tergantung pada API. Beberapa API, seperti Claude API, memberi tahu Anda untuk mencoba kembali setelah kesalahan 500 dengan backoff eksponensial. Periksa dokumentasi API.
502 Bad Gateway Gateway atau proksi mendapat respons yang tidak valid dari server di belakangnya Ya, jika permintaan aman untuk diulang
503 Service Unavailable Server sementara kelebihan beban atau tidak tersedia karena pemeliharaan, dan harus pulih setelah beberapa waktu. Server dapat mengirim Retry-After header. Ya, setelah waktu Retry-After jika server mengirimkannya
504 Gateway Timeout Gateway atau proksi tidak mendapatkan respons tepat waktu dari server di belakangnya Ya, jika permintaan aman untuk diulang. Gateway menyerah menunggu, jadi Anda tidak tahu apakah server melakukan pekerjaan.

Permintaan mana yang aman untuk dicoba lagi

RFC 9110 menyebut suatu metode idempoten ketika mengirim permintaan yang sama beberapa kali memberikan efek yang sama seperti mengirimnya sekali. GET, HEAD, OPTIONS, TRACE, PUT, dan DELETE bersifat idempoten. POST dan PATCH tidak. Menurut RFC, klien tidak boleh secara otomatis mencoba kembali permintaan dengan metode non-idempoten kecuali tahu bahwa permintaan itu idempoten pula, atau dapat mengetahui bahwa server tidak pernah menerapkan permintaan asli. Untuk detailnya, lihat Metode idempoten.

Percobaan ulang POST setelah 502 atau 504 dapat membuat pesanan kedua atau mengirim email kedua. Beberapa pustaka yang mencoba ulang akan mencoba kembali setiap metode secara default. Misalnya, handler ketahanan standar .NET mencoba ulang POST kecuali Anda memanggil options.Retry.DisableForUnsafeHttpMethods(). Untuk detailnya, lihat Membangun aplikasi HTTP tangguh.

Retry-After pada 503

503 dapat menyertakan Retry-After header. Nilainya adalah sejumlah detik, seperti 120, atau tanggal HTTP, seperti Fri, 31 Dec 1999 23:59:59 GMT. Kode Anda perlu menangani keduanya. Untuk detailnya, lihat Retry-After.

Berhenti memanggil API yang terus gagal

Percobaan ulang membantu mengatasi kegagalan singkat. Saat API tidak berfungsi selama beberapa menit, mencoba kembali setiap permintaan menambahkan beban ke server yang sudah kewalahan, dan pengguna Anda menunggu setiap percobaan ulang gagal. Circuit breaker melacak kegagalan, dan ketika jumlahnya terlalu banyak, circuit breaker berhenti memanggil API untuk sementara waktu dan gagal dengan cepat. Setelah waktu itu, sistem mengizinkan beberapa permintaan lewat untuk memeriksa apakah API telah pulih. Untuk informasi selengkapnya, lihat Pola Pemutus Sirkuit. standard resilience handler .NET mencakup circuit breaker yang terbuka selama 5 detik ketika setidaknya 10% permintaan gagal dalam jangka waktu 30 detik dengan setidaknya 100 permintaan.

Cara menangani kesalahan 5xx

  1. Coba lagi 502, 503, dan 504 hanya untuk permintaan idempoten. Untuk POST dan PATCH, coba lagi hanya jika API mendokumentasikan cara untuk membuatnya aman untuk diulang.
  2. Tunggu sebelum Anda mencoba kembali. Gunakan Retry-After saat server mengirimkannya. Jika tidak, gunakan backoff eksponensial dengan jitter acak, dan hentikan setelah beberapa upaya.
  3. Baca dokumen API untuk kode 500. Coba lagi hanya jika API mengatakan bahwa aman untuk mencobanya lagi.
  4. Berhenti memanggil API yang terus gagal. Gunakan circuit breaker sehingga aplikasi Anda gagal dengan cepat saat API pulih.
  5. Beri tahu pengguna apa yang terjadi. Perlihatkan "layanan mengalami masalah, coba lagi nanti" alih-alih kesalahan umum atau jejak tumpukan.
const RETRYABLE_STATUS = new Set([502, 503, 504]);
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"]);

function retryAfterMs(response) {
  const value = response.headers.get("retry-after");
  if (!value) return null;
  const seconds = Number(value);
  if (Number.isFinite(seconds)) return seconds * 1000;
  const date = Date.parse(value);
  return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
}

export async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  const method = (options.method ?? "GET").toUpperCase();
  const canRetry = IDEMPOTENT_METHODS.has(method);

  for (let attempt = 0; ; attempt++) {
    const response = await fetch(url, options);
    if (!RETRYABLE_STATUS.has(response.status) || !canRetry || attempt === maxRetries) {
      return response;
    }
    await response.body?.cancel();
    const backoff = 2 ** attempt * 1000 + Math.random() * 1000;
    const wait = retryAfterMs(response) ?? backoff;
    await new Promise((resolve) => setTimeout(resolve, wait));
  }
}

Cara menguji bahwa aplikasi Anda menangani kesalahan 5xx

Anda jarang melihat 5xx saat mengembangkan, dan Anda tidak dapat membuat API gagal sesuai permintaan. Cara Anda menguji menentukan apakah Anda menemukan bug sebelum pengguna Anda melakukannya.

Approach Apa yang Anda temukan Yang Anda lewatkan
Tunggu hingga penerapan selesai Gangguan aktual Semuanya, sampai pengguna menekannya
Mock API dalam pengujian Anda, atau biarkan agen pengkodan Anda menulis mock Apakah cabang penanganan error Anda dijalankan Klien HTTP asli Anda dan pustaka retry, serta berapa kali sebenarnya pustaka tersebut melakukan percobaan ulang. Aplikasi Anda juga memerlukan opsi khusus pengujian untuk mengakses mock.
Panggil API nyata dan tunggu hingga gagal Perilaku sebenarnya Anda tidak dapat membuat API gagal secara sengaja
Mencegat lalu lintas nyata aplikasi Anda dan mengembalikan kesalahan 5xx dengan tingkat yang Anda pilih Klien HTTP asli Anda, pustaka percobaan ulang, dan pemutus sirkuit Tidak ada yang berubah di aplikasi Anda, sehingga tidak menguji kode Anda secara terisolasi. Simpan pengujian unit Anda untuk itu.

Coba ini di aplikasi Anda

Dev Proxy mencegat permintaan aplikasi Anda dan membuat sebagian permintaan tersebut gagal dengan kesalahan yang Anda tentukan, menggunakan GenericRandomErrorPlugin. Aplikasi Anda terus memanggil URL nyata. Tambahkan plugin ke file konfigurasi Anda dan atur errorsFile-nya agar merujuk ke file dengan kesalahan 5xx. Contoh ini menggunakan https://api.contoso.com. Ganti dengan URL API yang dipanggil aplikasi Anda.

File: server-errors.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
  "errors": [
    {
      "request": {
        "url": "https://api.contoso.com/*"
      },
      "responses": [
        { "statusCode": 500 },
        { "statusCode": 502 },
        {
          "statusCode": 503,
          "headers": [
            { "name": "Retry-After", "value": "10" }
          ]
        },
        { "statusCode": 504 }
      ]
    }
  ]
}

Secara default, plugin gagal pada 50% permintaan. Periksa di output Proksi Dev bahwa aplikasi Anda mencoba kembali permintaan GET dan mengirim setiap POST hanya sekali. Dev Proxy tidak memeriksa apakah aplikasi Anda menunggu sebelum mencoba lagi setelah menerima respons 503, jadi bandingkan sendiri waktu permintaannya. Kemudian mulai Dev Proxy dengan --failure-rate 100 untuk melihat apa yang dilakukan aplikasi Anda saat API terus gagal. Untuk informasi selengkapnya, lihat Tingkat kegagalan permintaan perubahan. Untuk menginstal Dev Proxy, lihat Menyiapkan Dev Proxy.

Langkah berikutnya

Baca juga