ERETEK
BİLGİ SİSTEMLERİ

API Entegrasyonlarında Hata Yönetimi: Retry, Backoff ve Circuit Breaker

API retry ve backoff politikası: durum koduna göre yeniden deneme, jitter, Retry-After, 429 kota yönetimi, circuit breaker ve dead-letter; sahadaki hatalar.

Bu makalede7 bölüm
Teknik özet
Teknoloji
HTTP, RFC 9110, RFC 6585, Exponential backoff, Circuit breaker, Dead-letter queue
Konu
Giden API çağrılarında hata sınıflandırma ve yeniden deneme politikası
Seviye
Orta–ileri
Ön koşullar
  • HTTP durum kodları bilgisi
  • En az bir dilde HTTP istemcisi kullanma deneyimi

Dış bir API’ye yapılan her çağrı er ya da geç başarısız olur: ağ kopar, karşı sistem bakıma girer, kota dolar ya da gönderdiğiniz veri reddedilir. Sahada en çok zarar veren hata çoğu zaman ilk arıza değil, ona verilen tepkidir: kalıcı hata sonsuza kadar denenir, çöken sisteme saniyede yüzlerce istek gider, zaman aşımına uğrayan POST körlemesine tekrarlanır ve mükerrer sipariş oluşur. ERP, CRM, ödeme ve Microsoft 365 gibi sistemlere giden çağrılar için retry, backoff, rate limit, zaman aşımı ve circuit breaker politikası baştan yazılı olmalıdır.

Geçici ve kalıcı hataları ayırmak

İlk kural: yalnızca geçici hatalar yeniden denenir. Geçici hata, aynı istek bir süre sonra değişmeden gönderildiğinde başarılı olma ihtimali olan hatadır: bağlantı zaman aşımı, DNS çözümleme hatası, karşı sistemin aşırı yüklenmesi, kota aşımı. Kalıcı hata ise istek değişmedikçe sonucu değişmeyecek olandır: geçersiz alan, yetkisiz erişim, bulunamayan kayıt.

Kalıcı hatayı yeniden denemek iki zarar doğurur. Birincisi, gerçek sorunu (eksik alan eşleştirmesi, süresi dolmuş sertifika) geç fark edersiniz; log, aynı hatanın binlerce tekrarıyla dolar ve asıl satır kaybolur. İkincisi, toplu aktarımda binlerce kaydın her biri birkaç kez denenince karşı sistemin kotası boşa harcanır ve sağlam istekler de etkilenir. Tek kötü kayıt bütün akışı kilitler.

İkinci kural: yeniden denenen işlem idempotent olmalı. RFC 9110 GET, PUT ve DELETE yöntemlerini idempotent tanımlar; POST değildir. Zaman aşımına uğrayan POST karşı tarafta işlenmiş olabilir. İstemci yanıtı alamadı diye işlem olmadı sanmayın. Sipariş veya ödeme oluşturan POST çağrıları, yalnızca karşı API bir idempotency anahtarı (birçok sağlayıcıda Idempotency-Key başlığı) ya da dış referans numarasıyla tekrar kontrolü destekliyorsa otomatik yeniden denenmelidir.

HTTP durum kodlarına göre yeniden deneme tablosu

Durum Anlamı Yeniden dene?
Bağlantı hatası, zaman aşımı Yanıt alınamadı Evet; POST için yalnız idempotency anahtarıyla
400, 422 İstek geçersiz Hayır; veriyi düzelt
401 Kimlik doğrulama başarısız Token yenileyip bir kez; sonra hayır
403, 404 Yetki yok / kaynak yok Hayır
408 Sunucu isteği beklerken zaman aşımı Evet
409 Çakışma Genellikle hayır; durumu okuyup karar ver
429 Çok fazla istek Evet; Retry-After değerine uy
500 Sunucu iç hatası Sınırlı sayıda
502, 503, 504 Ağ geçidi / hizmet erişilemez Evet, backoff ile

Bu tablo bir varsayılandır, sözleşme değil. Bazı API’ler doğrulama hatasında 500 döner, bazıları kotayı 403 ile bildirir. Entegre olduğunuz her API için belgeyi okuyun, tabloyu o API’ye göre yapılandırın ve yanıt gövdesindeki hata kodunu da sınıflandırmaya katın. 500’ün sınırlı denenmesinin nedeni, bu kodun hem geçici bir arızayı hem de belirli bir isteğin her seferinde tetiklediği bir yazılım hatasını gösterebilmesidir. Aynı kayıt her seferinde 500 alıyorsa sorun karşı tarafta değil, o kayıttadır.

Exponential backoff, jitter ve Retry-After

Hata alınınca hemen ve sabit aralıkla yeniden denemek, sorunlu sistemi toparlanmaya fırsat vermeden yeniden yükler. Exponential backoff her denemede bekleme süresini katlayarak artırır. Tek başına yetmez: aynı anda hata alan yüzlerce istemci aynı aralıklarla tekrar denerse yük dalgalar hâlinde geri gelir. Jitter bekleme süresine rastgelelik ekleyerek bu senkronizasyonu dağıtır. Yaygın “full jitter” yaklaşımında bekleme, sıfır ile üstel üst sınır arasında rastgele seçilir.

TABAN = 1.0      # saniye
TAVAN = 30.0     # tek beklemenin üst sınırı
MAKS_DENEME = 5

for deneme in range(MAKS_DENEME + 1):
    yanit = cagir(istek, timeout=kalan_sure_butcesi())
    if yanit.basarili:
        return yanit
    if not yeniden_denenebilir(yanit) or deneme == MAKS_DENEME:
        raise KaliciHata(yanit)
    if yanit.retry_after is not None:          # 429 / 503
        bekle = yanit.retry_after
    else:
        bekle = random.uniform(0, min(TAVAN, TABAN * 2 ** deneme))
    if bekle > kalan_sure_butcesi():
        raise SureButcesiAsildi()
    uyu(bekle)

Örnek hesap: Taban 1 saniye ile beş yeniden denemeden önceki beklemelerin üst sınırları sırasıyla 1, 2, 4, 8 ve 16 saniyedir; toplam bekleme en kötü durumda 31 saniye, ilk çağrıyla birlikte altı çağrının zaman aşımları da eklenince bunun üzerindedir. 30 saniyelik tavan bu ayarda devreye girmez; deneme sayısı artırılırsa altıncı yeniden denemeden itibaren her bekleme 30 saniyeyle sınırlanır. Kullanıcı isteğinin içinde çalışan bir çağrı için bu çok uzundur. Arka plan senkronizasyonu için makuldür. Parametreleri çağrının bağlamına göre ayrı belirleyin; tek bir global ayar iki tarafı da bozar.

Retry-After başlığı RFC 9110’da tanımlıdır ve saniye cinsinden bir sayı ya da HTTP tarihi taşıyabilir. İstemci iki biçimi de ayrıştırmalı ve sunucunun önerdiği süreden kısa beklememelidir. Bu başlık geldiğinde kendi backoff hesabınız yerine sunucunun değerini kullanın; kota pencerelerinde gereksiz reddedilmeleri önler. Sahada sık gördüğümüz hata, yalnız sayı biçimini ayrıştıran istemcinin tarih biçiminde sıfır saniye bekleyip hemen tekrar denemesidir.

Rate limit ve kota yönetimi

429 (Too Many Requests) durum kodu RFC 6585 ile tanımlıdır. Kota hatasına yalnız tepki vermek yerine kotayı istemci tarafında önceden yönetin:

  • Kotayı belgeden öğrenin ve istemcide sınırlayın. Token bucket gibi bir hız sınırlayıcıyla saniye veya dakika başına çağrı sayısını kotanın altında tutun.
  • Kalan kota başlıklarını okuyun. Birçok API kalan istek sayısını ve pencerenin sıfırlanma zamanını X-RateLimit-Remaining benzeri başlıklarla bildirir. Bu başlıklar standart değildir; adları ve anlamları API’ye göre değişir.
  • Kotayı paylaşan tüm süreçleri birlikte düşünün. Aynı API anahtarını kullanan gece aktarımı, webhook işçisi ve web sitesi aynı kotayı tüketir. Gece ürün senkronu kotayı bitirir, sabah siparişler 429 alır. Kritik akışlara (sipariş) öncelik, toplu akışlara (ürün senkronu) düşük öncelik verin.
  • Toplu uç noktaları tercih edin. API tek çağrıda birden fazla kaydı güncelleyen bir batch uç noktası sunuyorsa kayıt başına çağrı yerine onu kullanın.

Microsoft Graph da kısıtlama (throttling) durumunda 429 ve Retry-After döner; Graph API entegrasyonlarında bu başlığa uymak kısıtlamanın uzamasını önlemenin temel yoludur. Başlığı yok sayıp ısrar eden istemci daha uzun bekler.

Zaman aşımı bütçesi ve circuit breaker

Zaman aşımı tanımlanmamış bir HTTP istemcisi yanıt vermeyen sunucuyu çok uzun süre bekleyebilir; birçok kütüphanenin varsayılanı ya çok uzundur ya da hiç yoktur. Kod incelemesinde ilk bakılacak yer burasıdır. İki ayrı değer belirleyin: bağlantı zaman aşımı (TCP/TLS kurulumu) kısa, okuma zaman aşımı API’nin normal yanıt süresine göre.

Tekil zaman aşımlarının yanında bir toplam süre bütçesi tanımlayın. Kullanıcının beklediği bir web isteğinin örneğin 3 saniyelik bütçesi varsa ve ilk deneme 2,5 saniye sürdüyse, ikinci deneme yapılmamalı ya da kalan süreyle sınırlı yapılmalıdır. Yukarıdaki pseudo-koddaki kalan_sure_butcesi() fonksiyonu bunun içindir.

Karşı sistem tamamen çöktüğünde her isteğin zaman aşımını beklemesi kendi uygulamanızın iş parçacıklarını ve bağlantı havuzunu tüketir. Arıza böyle yayılır. Circuit breaker bu durumu üç hâlli bir mekanizmayla yönetir:

Hâl Davranış Geçiş koşulu
Kapalı (closed) Çağrılar normal yapılır, hatalar sayılır Hata oranı eşiği aşılırsa açık
Açık (open) Çağrı yapılmadan hemen hata dönülür Bekleme süresi dolunca yarı açık
Yarı açık (half-open) Sınırlı sayıda deneme çağrısına izin verilir Başarılıysa kapalı, değilse açık

Breaker açıkken kullanıcıya anlamlı bir yedek davranış sunun: önbellekteki son fiyatı “teyit edilecek” notuyla göstermek ya da siparişi kuyruğa alıp “işleniyor” durumunda bırakmak gibi. Desenin ayrıntılı tarifi Microsoft’un Circuit Breaker pattern belgesinde bulunur. Retry ile circuit breaker birlikte kullanıldığında retry döngüsü breaker açıkken denemeyi durdurmalıdır; aksi hâlde iki mekanizma birbirinin etkisini bozar.

Başarısız mesajlar için dead-letter

Arka plan entegrasyonlarında deneme hakkı tükenen mesaj sessizce silinmez. Dead-letter kuyruğu (DLQ), işlenemeyen mesajların hata nedeniyle birlikte bekletildiği ayrı bir alandır. RabbitMQ, Azure Service Bus ve Amazon SQS gibi aracılar bu kavramı yerleşik sunar; veritabanı tablosuyla çalışan basit kuyruklarda bir status = 'dead' alanı aynı işi görür. Silinen mesaj geri gelmez. Dead-letter’daki mesaj düzeltilip yeniden oynatılır.

Dead-letter yönetimi için kontrol listesi:

  • Mesajla birlikte son hata kodu, hata mesajı, deneme sayısı ve ilk/son deneme zamanı saklanıyor mu?
  • Kalıcı hatalar (400, 422) yeniden deneme beklemeden doğrudan dead-letter’a gidiyor mu?
  • Dead-letter’daki mesaj sayısı için alarm tanımlı mı ve sorumlusu belli mi?
  • Sorun giderildikten sonra mesajları ana kuyruğa geri göndermenin (replay) belgelenmiş bir yolu var mı?
  • Replay edilen mesajların idempotent işlendiği doğrulandı mı?

Dead-letter kuyruğunun boyutu ve yaşı, entegrasyonun sağlığını gösteren ana metriklerden biridir; bu metriklerin alarm tasarımı entegrasyon izleme ve mutabakat rehberinde anlatılıyor. Gelen trafik tarafında aynı ilkelerin karşılığı için webhook alıcısı tasarımı, hangi akışın senkron hangisinin kuyruklu olacağı için ERP–web entegrasyon desenleri yazılarına bakabilirsiniz. Mevcut entegrasyonlarınızın hata politikasını gözden geçirmek isterseniz ERP, CRM ve API entegrasyonları sayfamızdan bilgi alabilirsiniz.

Sık sorulan sorular

Kaç kez yeniden deneme yapılmalı?

Evrensel bir sayı yok; belirleyici olan toplam süre bütçesidir. Kullanıcının beklediği isteklerde bir veya iki kısa deneme yeter. Arka plan aktarımlarında daha fazla deneme yapılabilir, ancak hak tükendiğinde mesaj silinmez, dead-letter alanına alınır.

POST isteği zaman aşımına uğradığında yeniden göndermek güvenli mi?

Yalnızca karşı API bir idempotency anahtarını ya da dış referans numarasıyla tekrar kontrolünü destekliyorsa güvenlidir. Aksi hâlde ilk istek karşı tarafta işlenmiş olabilir ve yeniden gönderim mükerrer sipariş ya da ödeme oluşturur. Bu durumda önce kaydın var olup olmadığını sorgulayın.

Jitter neden gerekli, sadece exponential backoff yetmez mi?

Aynı anda hata alan çok sayıda istemci aynı üstel aralıklarla yeniden denerse istekler yine aynı anlarda yığılır ve toparlanmaya çalışan sistemi dalgalar hâlinde yükler. Jitter bekleme süresine rastgelelik ekleyerek bu istekleri zamana yayar.

Circuit breaker ile retry birlikte nasıl çalışmalı?

Retry tekil bir isteğin geçici hatasını tolere eder; circuit breaker ise karşı sistem genel olarak çöktüğünde çağrıları tamamen durdurur. Retry döngüsü her denemeden önce breaker durumuna bakmalı, breaker açıksa beklemeden hata dönmeli ve yedek davranışa geçmelidir.

Teknik özet
Teknoloji
HTTP, RFC 9110, RFC 6585, Exponential backoff, Circuit breaker, Dead-letter queue
Konu
Giden API çağrılarında hata sınıflandırma ve yeniden deneme politikası
Seviye
Orta–ileri
Ön koşullar
  • HTTP durum kodları bilgisi
  • En az bir dilde HTTP istemcisi kullanma deneyimi
Eretek uzman desteği

Bu konu sizin ortamınızda nasıl çalışır, birlikte bakalım.

Mevcut yapınızı inceliyor, darboğazı tespit ediyor ve uygulanabilir bir iş planı öneriyoruz.