Robust Webhook Retry Stratejisi Nasıl Kurulur?
Web uygulamalarında veri akışını kesintisiz sağlamak için robust (dayanıklı) bir webhook retry (yeniden deneme) stratejisi kurmak hayati önem taşır.
Webhook Nedir ve Neden Başarısız Olur?
Webhooks (web kancaları), modern web mimarisinde uygulamaların birbiriyle gerçek zamanlı olarak haberleşmesini sağlayan hafif yöntemlerdir. Temel olarak, bir sistemde belirli bir olay (event) gerçekleştiğinde, diğer sisteme HTTP POST isteği gönderilmesi mantığına dayanır. Örneğin, bir e-ticaret platformunda ödeme tamamlandığında, kargo sistemine otomatik bilgi gönderilmesi bu yöntemle gerçekleşir. Ancak, internet dünyası her zaman kararlı ve kesintisiz değildir. Bu nedenle, webhook entegrasyonu (uyumlaştırma) süreçlerinde sık sık kesintiler yaşanır.
Öncelikle, hedef sunucuda anlık bir çökme meydana gelebilir. Bununla birlikte, ağ bağlantısındaki geçici kopmalar veya sunucunun aşırı yük altında kalması (rate limiting – istek sınırlama) gibi durumlar webhook gönderiminin başarısız olmasına yol açar. Eğer gönderici sistem, alıcının isteği alamadığını fark edip işlemi öylece bırakırsa, veri kaybı kaçınılmaz olur. Örneğin, iyzico gibi bir ödeme geçidi üzerinden alınan bir ödemenin başarılı bilgisi e-ticaret sitenize ulaşmazsa, müşterinin siparişi onaylanmaz. Bu durum hem müşteri memnuniyetsizliğine hem de operasyonel karmaşaya neden olur. İşte tam bu noktada, akıllı ve hata toleransı (fault tolerance) yüksek bir yeniden deneme mekanizması kurmak zorunlu hale gelir.
Exponential Backoff (Üstel Geri Çekilme) Nedir ve Nasıl Çalışır?
Bir webhook başarısız olduğunda, en sık yapılan hata sistemi hemen ardından tekrar tekrar aramaktır. Örneğin, her 5 saniyede bir aynı isteği göndermek mantıklı görünebilir. Ancak, hedef sunucu zaten aşırı yük altındaysa veya geçici bir veritabanı kilitlemesi yaşıyorsa, bu sık aralıklarla yapılan istekler sunucunun daha da çökmesine yol açar. Yazılım dünyasında bu duruma “Thundering Herd Problem” (Gürültülü Sürü Problemi) adı verilir.
Bu problemi çözmek için Exponential Backoff (üstel geri çekilme) algoritması kullanmalısınız. Bu algoritma, her başarısız denemeden sonra bekleme süresini katlayarak artırır. Böylece, karşı sunucuya kendini toparlaması için zaman tanımış olursunuz. Matematiksel olarak formül şu şekildedir:
Bekleme Süresi = Başlangıç Gecikmesi * (Çarpan ^ Deneme Sayısı)
Örneğin, başlangıç gecikmesini 2 saniye, çarpanı ise 2 olarak belirleyelim. Bu durumda deneme süreçleri şu şekilde ilerler:
- 1. Deneme: Başarısız. 2 saniye bekle.
- 2. Deneme: Başarısız. 4 saniye bekle.
- 3. Deneme: Başarısız. 8 saniye bekle.
- 4. Deneme: Başarısız. 16 saniye bekle.
Görüldüğü üzere, her adımda süre iki katına çıkar. Bu sayede, hedef sunucunun üzerindeki baskı kademeli olarak azaltılır. Ancak, tüm başarısız isteklerin aynı anda tekrar denemeye başlamasını engellemek için bu formüle “Jitter” (rastgele sapma) eklemek gerekir.
Jitter (Rastgele Sapma) Neden Eklenmelidir?
Sistemlerimizde sadece üstel geri çekilme kullanmak bazen yeterli olmayabilir. Çünkü aynı anda çöken yüzlerce webhook isteği, aynı gecikme sürelerini hesaplayarak hedef sunucuya tam olarak aynı saniyede tekrar hücum edebilir. Bu senaryo, hedef sunucunun ayağa kalktığı anda tekrar çökmesine sebep olur.
Jitter (rastgele sapma), bekleme süresine rastgele bir gürültü veya zaman dilimi ekleme işlemidir. Örneğin, hesaplanan 8 saniyelik bekleme süresine rastgele +/- 1.5 saniye ekleyerek süreyi 6.5 ile 9.5 saniye arasına çekebilirsiniz. Böylelikle, kuyruktaki yüzlerce webhook isteği farklı milisaniyelerde tetiklenir. Bu durum, hedef sunucunun gelen trafiği daha dengeli ve pürüzsüz bir şekilde işlemesini sağlar. Sonuç olarak, ağ trafiğindeki tepe noktaları (peaks) düzleştirilir ve sistem genelinde yüksek bir hata toleransı elde edilir.
Adım Adım Webhook Retry Mekanizması Nasıl Tasarlanır?
Güvenilir bir webhook mimarisi tasarlarken, işlemleri kesinlikle ana uygulama akışında (senkron olarak) yapmamalısınız. Bunun yerine, asenkron (eşzamansız) bir arka plan işleyicisi kullanmalısınız. Bir webhook tetiklendiğinde, bu istek hemen bir mesaj kuyruğuna (message broker) aktarılmalıdır.
Tasarım sürecinde şu adımları izleyebilirsiniz:
- Veritabanı Kaydı: Gönderilecek her webhook isteğini benzersiz bir kimlik (ID) ile veritabanına kaydedin ve durumunu “PENDING” (Beklemede) olarak işaretleyin.
- Kuyruğa Ekleme: İsteği Redis, RabbitMQ veya Amazon SQS gibi bir kuyruk sistemine gönderin.
- İşleme ve İzleme: Kuyruk tüketicisi (consumer) isteği hedef sunucuya gönderir. Eğer HTTP 200 OK dönerse, durumu “SUCCESS” (Başarılı) yapın.
- Hata Yakalama ve Hesaplama: Eğer sunucu 5xx hatası veya zaman aşımı (timeout) verirse, deneme sayısını 1 artırın. Exponential backoff ve jitter formülünü kullanarak yeni bir gönderim zamanı hesaplayın.
- Gecikmeli Yeniden Deneme: İsteği, hesaplanan süre kadar geciktirerek kuyruğa yeniden ekleyin.
- DLQ (Dead Letter Queue) Aktarımı: Maksimum deneme sınırına (örneğin 5 veya 10 kez) ulaşıldığında, isteği “FAILED” (Başarısız) olarak işaretleyin ve manuel inceleme için Dead Letter Queue (Alınamayan Mesaj Kuyruğu) havuzuna aktarın.
Aşağıdaki tablo, tipik bir 5 adımlı retry sürecinin zaman akışını göstermektedir:
| Deneme No | Temel Süre (sn) | Jitter Aralığı (sn) | Toplam Gecikme (Yaklaşık) | Durum |
|---|---|---|---|---|
| 1 | 5 | +/- 1 | 4 – 6 saniye | Yeniden Deneniyor |
| 2 | 10 | +/- 2 | 8 – 12 saniye | Yeniden Deneniyor |
| 3 | 20 | +/- 4 | 16 – 24 saniye | Yeniden Deneniyor |
| 4 | 40 | +/- 8 | 32 – 48 saniye | Yeniden Deneniyor |
| 5 | 80 | +/- 16 | 64 – 96 saniye | Son Deneme / DLQ |
Node.js ve Redis ile Örnek Bir Uygulama Nasıl Yapılır?
Bu bölümde, popüler bir JavaScript çalışma ortamı (runtime) olan Node.js kullanarak basit ama etkili bir üstel geri çekilme algoritması yazacağız. Bu kod örneğinde, hata durumunda bekleme süresini hesaplayan fonksiyonu ve kuyruk yönetim mantığını görebilirsiniz.
// Exponential Backoff ve Jitter Hesaplama Fonksiyonu
function calculateDelay(attempt, baseDelay = 1000, maxDelay = 30000) {
// Üstel gecikmeyi hesapla: base * 2^attempt
const tempDelay = baseDelay * Math.pow(2, attempt);
// Maksimum gecikme sınırını aşmamasını sağla
const backoffLimit = Math.min(tempDelay, maxDelay);
// Jitter (Rastgele Sapma) ekle (%20 oranında)
const jitter = backoffLimit * 0.2 * Math.random();
// Nihai bekleme süresi
return Math.floor(backoffLimit + jitter);
}
// Örnek Webhook Gönderim Fonksiyonu
async function sendWebhookWithRetry(webhookUrl, payload, attempt = 0, maxAttempts = 5) {
try {
console.log(Deneme ${attempt + 1}: Webhook gönderiliyor...);
const response = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(Sunucu Hatası: ${response.status});
}
console.log("Webhook başarıyla iletildi!");
return true;
} catch (error) {
console.error(Hata Oluştu: ${error.message});
if (attempt < maxAttempts) {
const delay = calculateDelay(attempt);
console.log(${delay} ms sonra tekrar denenecek...\n);
// Belirlenen süre kadar asenkron olarak bekle ve yeniden dene
await new Promise(resolve => setTimeout(resolve, delay));
return sendWebhookWithRetry(webhookUrl, payload, attempt + 1, maxAttempts);
} else {
console.error("Maksimum deneme sınırına ulaşıldı. İstek DLQ kuyruğuna aktarılıyor.");
return false;
}
}
}
// Fonksiyonu Test Etme (Sürekli 503 hatası veren test servisi)
const targetUrl = "https://httpstat.us/503";
const data = { orderId: 12345, status: "completed" };
sendWebhookWithRetry(targetUrl, data);
Yukarıdaki kod bloğunda gördüğünüz üzere, calculateDelay fonksiyonu her başarısız denemede süreyi katlayarak artırır ve üzerine rastgele bir sapma ekler. Bu sayede, hedef sunucunun anlık kesintilerinden etkilenmeden, sistem kaynaklarını en verimli şekilde kullanmış oluruz.
Gerçek Dünya Senaryosu: Trendyol Entegrasyonunda Ödeme Kaybı
Gelin konuyu daha net anlamak için yerel e-ticaret pazarından bir örnek üzerinden ilerleyelim. Farz edelim ki, Trendyol veya Hepsiburada üzerinde satış yapan bir mağazanız var ve kendi ERP (Kurumsal Kaynak Planlaması) sisteminizi bu platformlara entegre ettiniz. Müşteri sipariş verdiğinde, Trendyol sunucuları sizin ERP sisteminize bir webhook gönderir.
Bir gün, ERP sisteminizin kurulu olduğu sunucu, yoğun bir veritabanı yedekleme işlemi nedeniyle 5 dakika boyunca yanıt veremez hale geldi. Bu sırada Trendyol’dan gelen 50 adet sipariş webhook isteği, sunucunuzdan “504 Gateway Timeout” hatası aldı. Eğer Trendyol tarafında akıllı bir retry mekanizması olmasaydı, bu 50 sipariş sizin sisteminize asla düşmeyecekti. Sonuç olarak, müşterilerin kargoları hazırlanamayacak, mağaza puanınız düşecek ve ciddi bir ciro kaybı yaşayacaktınız.
Ancak, üstel geri çekilme kullanan bir webhook sistemi sayesinde, ilk başarısız denemeden sonra istekler hemen silinmez. Sistem önce 10 saniye, sonra 30 saniye, ardından 2 dakika bekleyerek istekleri yeniden gönderir. Sunucunuz yedekleme işlemini tamamlayıp tekrar ayağa kalktığında, biriken tüm webhook istekleri güvenli bir şekilde ERP sisteminize ulaşır ve siparişleriniz kesintisiz şekilde işlenmeye devam eder.
Webhook Güvenliği ve İleri Düzey İpuçları Nelerdir?
Dayanıklı bir webhook retry stratejisi kurarken, güvenliği ve veri tutarlılığını asla göz ardı etmemelisiniz. Yeniden deneme mekanizmaları doğası gereği aynı isteğin birden fazla kez gönderilmesine yol açabilir. Bu durum, sistemlerinizde mükerrer (tekrarlanan) işlemlere sebep olabilir.
Bu sorunları önlemek için şu ileri düzey teknikleri uygulayabilirsiniz:
- Idempotency (Aynı Güçlü İşlem / Tekrarlanabilirlik): Alıcı taraf, gelen her webhook isteğindeki benzersiz işlem ID’sini (örneğin sipariş numarası) kontrol etmelidir. Eğer bu ID daha önce başarıyla işlendiyse, işlemi tekrar yapmadan doğrudan HTTP 200 OK dönmelidir. Bu, sisteminizin mükerrer ödeme almasını veya aynı kargoyu iki kez çıkarmasını engeller.
- Webhook İmzaları (Signature Verification): Gönderdiğiniz webhook isteklerinin gövdesini (payload) gizli bir anahtar (secret key) ile imzalayın. Alıcı, gelen isteğin gerçekten sizden geldiğini doğrulamak için bu imzayı kontrol etmelidir.
- Zaman Aşımı Sınırları (Timeout Limits): İstek gönderirken bağlantı zaman aşımını (connection timeout) kısa tutun (örneğin 5-10 saniye). Yanıt vermeyen bir sunucuyu dakikalarca beklemek, sizin kendi kuyruk sisteminizi kilitleyebilir.
- Kuyruk İzolasyonu (Queue Isolation): İlk denemeler için hızlı kuyruklar kullanırken, çok kez başarısız olmuş ve saatler sonra denenecek istekleri farklı bir yavaş kuyruğa (slow queue) aktarın. Böylece yeni ve sağlıklı webhook istekleri, eski ve sorunlu isteklerin arkasında beklemek zorunda kalmaz.
Sıkça Sorulan Sorular (FAQ)
Webhook istekleri için ideal maksimum deneme sayısı kaçtır?
Genellikle endüstri standartlarında maksimum deneme sayısı 5 ile 10 arasında değişir. Önemli olan deneme sayısından ziyade, toplam deneme süresidir. Exponential backoff kullanarak bu denemeleri 24 saate yaymak en sağlıklı yaklaşımdır.
Başarısız olan webhook’ları ne kadar süreyle saklamalıyız?
Maksimum deneme sınırına ulaşan ve DLQ (Dead Letter Queue) sistemine düşen webhook’lar, manuel analiz ve hata ayıklama süreçleri için en az 7 ila 14 gün boyunca veritabanında saklanmalıdır.
Alıcı sunucu 404 hatası veriyorsa yeniden denemeli miyiz?
Hayır. HTTP 404 (Bulunamadı) veya 401 (Yetkisiz) gibi 4xx serisi istemci hataları, genellikle kalıcı yapılandırma hatalarına işaret eder. Bu durumlarda yeniden deneme yapmak ağ trafiğini boşuna meşgul edecektir. Yeniden deneme mekanizması sadece 5xx (Sunucu Hataları) ve ağ zaman aşımı durumlarında tetiklenmelidir.
Kuyruk sistemi olarak ne tercih edilmelidir?
Küçük ve orta ölçekli projeler için Redis tabanlı BullMQ oldukça başarılıdır. Büyük ölçekli, yüksek trafikli sistemlerde ise RabbitMQ, Apache Kafka veya AWS SQS gibi gelişmiş mesaj kuyruğu sistemleri tercih edilmelidir.
#Teknoloji #WebGeliştirme #Backend #SoftwareArchitecture #API
