HTTP 429: Retry-After ve Yeniden Deneme Süresi Nasıl Yönetilir?

Sağlam bir 429 politikası Retry-After değerine uyar, üst sınır ve rastgelelik içeren üstel geri çekilme uygular, yeniden denemeleri süre, adet ve işlem güvenliğiyle sınırlar.

Sunucu HTTP 429 döndürdüğünde isteği hemen yinelemeyi bırakın. Geçerli bir Retry-After değeri varsa ona uyun; yoksa üst sınır ve rastgelelik içeren, giderek uzayan bekleme süreleri kullanın. Her yeni denemeden önce iptal sinyalini, işlemin güvenle yinelenip yinelenemeyeceğini ve ortak yeniden deneme bütçesini kontrol edin. Bütçe tükendiğinde daha fazla trafik üretmek yerine anlaşılır bir hatayla sonlandırın.

429 istemciye ne anlatır?

HTTP 429 Too Many Requests, istemcinin belirli bir zaman aralığında fazla sayıda istek gönderdiğini belirtir. Yanıtta Retry-After bulunabilir. Ancak durum kodu tek başına kota aralığını, sınırın kullanıcıya mı IP adresine mi uygulandığını veya kısa süre sonra yapılacak yeni bir denemenin başarıya ulaşıp ulaşmayacağını açıklamaz.

429'u sıradan bir geçici ağ hatası değil, yükü azaltmaya yönelik açık bir geri bildirim olarak ele alın. Sıkı bir yeniden deneme döngüsü, zaten kapasite sıkıntısı yaşayan hizmetin yükünü büyütür. Çok sayıda işleyici aynı anda yeniden denerse kapasite yeniden kullanılabilir hâle geldiği anda ikinci bir trafik dalgası oluşabilir.

Karar, çağrının türüne de bağlıdır. Etkileşimli bir isteğin gecikme payı dar olabilir. Arka plandaki eşitleme işi daha uzun süre bekleyebilir. Bir kuyruk tüketicisi ise her mesajı art arda denemek yerine eşzamanlı işlem sayısını azaltabilir.

Retry-After değerini okurken hatalı girdileri de hesaba katın

Retry-After iki standart biçimde gelir: Retry-After: 12 gibi negatif olmayan saniye değeri veya HTTP tarihi. Saniye biçimi doğrudan kullanılabilir. Tarih biçiminde güncel zamanla aradaki fark hesaplanır. İstemci ve sunucu saatleri uyuşmayabileceğinden, varsa yanıtın Date başlığını da hesaba katmak daha güvenlidir.

Sunucudan gelen bir değer sınırsız beklemeye dönüşmemelidir. Geçersiz ve negatif değerleri reddedin; istenen bekleme süresi işlemin kalan süresini aşıyorsa işlemi durdurun veya sonraya bırakın. Sunucunun geçerli bekleme süresini kısaltıp erken denemeyin. Başlık yoksa ya da ayrıştırılamıyorsa istemcinin geri çekilme politikasına dönün. Geçmişte kalmış bir HTTP tarihi taban gecikmeyi sıfıra indirebilir; yine de rastgelelik ve bütçe denetimi, istemcilerin aynı anda tekrar yüklenmesini önlemelidir.

function yenidenDenemeGecikmesi(yanit, deneme, simdi, kalanSure) {
  const onerilen = retryAfterAyristr(yanit.headers, simdi)
  const yedek = min(tabanGecikme * 2 ** deneme, gecikmeSiniri)
  const gecikme = onerilen.gecerli
    ? onerilen.ms + randomBetween(0, rastgelelikSiniri)
    : randomBetween(0, yedek)
  if (gecikme > gecikmeSiniri || gecikme >= kalanSure) return null
  return gecikme
}

Bu taslak belirli bir kütüphaneyi değil, bekleme kararını gösterir. null sonucu hemen yeniden denemek değil, işlemi durdurmak veya sonraya bırakmak demektir. Çağıran kod, isteğin kendisi için de süre ayırmalı ve iptal sinyaline uymalıdır. Üretimde geçen bütçe süresini ölçmek için monotonik saat; HTTP tarihini yorumlamak içinse duvar saati kullanın.

Geri çekilmeyi rastgelelikle birleştirin

Üstel geri çekilme, art arda denemelerin arasını açar. Örneğin gecikme bir taban aralıkla başlayıp iki, dört ve sekiz aralığa çıkabilir; ardından belirlenen üst sınırda kalır. Taban değer ile üst sınır, hizmet sözleşmesine ve kullanıcıya ayrılan toplam süreye göre seçilmelidir. Ödeme API'sindeki sabitleri bir toplu indeksleme işine aynen taşımak doğru değildir.

Rastgelelik, birlikte hata alan istemcilerin birlikte yeniden denemesini engeller. Tam rastgelelik, sıfır ile hesaplanan üst sınır arasından bir değer seçer. Eşit rastgelelik ise taban beklemenin bir bölümünü koruyup kalanını değiştirir. Tutarlı uygulandığında ikisi de işe yarayabilir; dağıtık sistemlerde asıl tehlikeli varsayılan, hiç rastgelelik kullanmamaktır.

Retry-After varsa belirtilen bekleme süresi dolmadan yeni istek göndermeyin. Temkinli bir istemci belirtilen süreden önce deneme yapmaz, sonrasına küçük bir rastgele yayılım ekler. Üst hizmet kesin bir anlam tanımlıyorsa uygulamanızı o sözleşmeye göre belgeleyin.

Bütçe yalnızca deneme sayısından ibaret değildir

Sabit bir deneme sınırı yararlıdır fakat tek başına yetmez. Otuzar saniye bekleyen üç deneme ile bir saniyede tamamlanan üç denemenin maliyeti aynı değildir. Bütçeyi birkaç boyutta tanımlayın:

Bütçe boyutuYeniden denemeden önce sorulacak soruUygulanacak karar
Deneme sayısıBu işlem kaç ek çağrı yaptı?Yapılandırılmış üst sınırda dur.
Geçen süreBekleme ve yeni istek, son tarihten önce bitebilir mi?Yeterli süre kalmadıysa sonlandır.
İstek hacmiTekrarlar istemci trafiğinin fazla bölümünü mü tüketiyor?Ortak token veya oran bütçesi kullan.
İşlem güvenliğiYineleme aynı yan etkiyi ikinci kez oluşturabilir mi?Tekrarlanan işlemleri tekilleştiren bir koruma kullan veya yeniden deneme.
İptalSonuca hâlâ ihtiyaç var mı?Beklemeyi ve süren çağrıyı hemen iptal et.

Çok sayıda çağrı birlikte hata aldığında ortak bütçe önem kazanır. Ortak sınır yoksa her işlem kendi kotasına uyarken uygulamanın bütünü bağımlı hizmeti boğabilir. Yeniden deneme kapasitesini token bucket, eşzamanlılık sınırı veya ilk denemede başarılı olan isteklere bağlı küçük bir payla kısıtlayın.

Yalnızca güvenle yinelenebilen işlemleri deneyin

Bir kaynağı okumak, sipariş oluşturmayı veya karttan ödeme almayı yinelemekten genellikle daha güvenlidir. Ancak yalnızca HTTP metodunun adına bakmak yetmez: güvenli görünen bir uç nokta beklenmedik yan etkiler yaratabilir; yazma yapan başka bir uç nokta ise tekilleştirme anahtarı destekleyebilir.

Durum değiştiren çağrıları ancak API güvenilir bir tekilleştirme mekanizması sunuyorsa yineleyin. İstemci aynı tekilleştirme anahtarını ve aynı gövdeyi tekrar kullanmalıdır; yeni anahtar, yeniden denemeyi yeni bir işleme dönüştürür. Kesin olarak alınan 429 yanıtıyla, sunucunun işlemi tamamlayıp tamamlamadığının bilinmediği bağlantı kopmasını da birbirinden ayırın.

Uygulama kontrol listesi

  • İşlemi sınıflandırın: Yinelemenin güvenli, tekilleştirme anahtarıyla koşullu olarak güvenli veya yasak olduğunu kaydedin.
  • İki başlık biçimini de destekleyin: Saniye değerini ve HTTP tarihini ayrıştırın; bozuk ya da negatif girdiyi reddedin.
  • Kesin sınırlar koyun: Deneme sayısını, tekil gecikmeyi, toplam geçen süreyi ve tekrar trafiğini sınırlayın.
  • Rastgelelik ekleyin: Süreçlerin ve sunucu örneklerinin zamanlamalarını birbirinden ayırın.
  • İptali iletin: Hem beklemenin hem ağ çağrısının kesilebilmesini sağlayın.
  • Talebi azaltın: Uygunsa eşzamanlılığı düşürün, kuyruktan alımı duraklatın veya işleri gruplayın.
  • Sonucu görünür kılın: Gizli bilgi sızdırmadan deneme sayısını, seçilen gecikmeyi, başlığın geçerliliğini, uç nokta sınıfını ve sonlandırma nedenini kaydedin.
  • Belirlenebilir testler yazın: Saat ve rastgele sayı kaynağını dışarıdan verin; eksik başlık, tarih, bozuk değer, üst sınır, iptal ve tükenen bütçe vakalarını sınayın.

Temel ölçüt şudur: Her yeniden deneme sınırlı bir bütçe tüketmeli ve başarı olasılığını artırmalıdır. Aynı talebi neredeyse aynı anda tekrarlıyorsa dayanıklılık sağlamaz; yalnızca ek yük üretir.

İşlemi tekrar göndermenin ne zaman güvenli olduğunu HTTP API’lerinde idempotency (İngilizce) yazısında inceleyebilirsiniz.

İlgili yazılar