Rate limiting, bir istemcinin belirli bir zaman penceresinde bir sunucuya ya da servise yapabileceği istek sayısını denetleyen ve sınırlayan temel bir trafik yönetimi mekanizmasıdır. Belirlenen sınır aşıldığında sistem genellikle istemciye HTTP 429 Too Many Requests durum kodu döndürerek işlemi reddeder veya isteği sıraya alır. Modern dağıtık mimarilerde hem hizmet sürekliliğini korumak hem de sunucu kaynaklarının dengeli tüketilmesini sağlamak amacıyla API ağ geçitlerinden uygulama katmanına kadar her aşamada uygulanır.

Hızlı Çözüm / Özet Adımlar (TL;DR)
- Mantık: İstemci kimliği (IP, API anahtarı veya oturum) başına izin verilen maksimum çağrı frekansını denetler.
- Temel Yanıt: Eşik aşıldığında istemciye HTTP 429 durum kodu ve genellikle tekrar deneme zamanını bildiren Retry-After başlığı döner.
- Yaygın Algoritmalar: Kısa patlamalara (burst) izin veren Token Bucket, trafiği sabit bir hızda yumuşatan Leaky Bucket ve zamana dayalı Pencere (Fixed/Sliding Window) modelleri.
- İstemci Stratejisi: Hata alındığında eşzamanlı istekleri kesmek, Retry-After süresine uymak ve üstel geri çekilme (exponential backoff) ile rastgele jitter eklemek.

Rate Limiting Neden Gerekir?

Yazılım mimarilerinde kaynaklar sınırlıdır. Veritabanı bağlantı havuzları, CPU iş parçacıkları, bellek ve ağ bant genişliği belirli bir eşzamanlı istek kapasitesine göre yapılandırılır. Rate limiting bulunmayan bir servis, istemcilerin kontrolsüz trafik dalgalanmaları karşısında doğrudan doyum noktasına ulaşır.

Kapasite Koruması ve Kaynak Tüketimi

Bir API uç noktası veritabanından ağır bir analitik sorgu çekiyor veya disk üzerinde yoğun girdi/çıktı (I/O) işlemi yürütüyor olabilir. Tek bir istemcinin saniyede onlarca kez bu ucu çağırması, arka plandaki kuyrukların şişmesine ve diğer tüm kullanıcıların isteklerinin zaman aşımına (HTTP 504) uğramasına yol açar. İstek sınırlama, her tüketiciye adil bir işlem payı (fair-share) vererek "noisy neighbor" (gürültücü komşu) probleminin önüne geçer.

Bu noktada üç kavramı birbirinden net bir şekilde ayırmak gerekir:

  • Rate Limiting: Belirli bir zaman biriminde (örneğin saniyede veya dakikada) kabul edilen azami çağrı adedini ifade eder.
  • Throttling (Hız Kısma): Eşik aşıldığında istekleri doğrudan reddetmek yerine kuyruğa alarak işlem hızını yapay olarak yavaşlatma sürecidir.
  • Quota (Dönemsel Kota): Genellikle aylık veya fatura dönemine bağlı olarak tanımlanan toplam çağrı bütçesidir (örneğin ayda 1.000.000 istek).

Kötüye Kullanım, Otomatik Trafik ve Maliyet Kontrolü

Yetkisiz veri kazıma (scraping), brute-force kimlik doğrulama denemeleri veya DoS dalgaları servisleri işlevsiz kılabilir. Ayrıca sunucusuz (serverless) fonksiyonlar, harici AI API çağrıları veya SMS/e-posta bildirim sağlayıcıları kullandıkça-öde (pay-as-you-go) modeliyle faturalandırılır. İstek sınırlama, bu tür otomatik döngülerin kontrolsüz fatura artışlarına dönüşmesini doğrudan engeller.

Temel İstek Sınırlama Algoritmaları

İstek trafiğini sınırlamak için kullanılan algoritmalar; bellek tüketimi, CPU karmaşıklığı ve trafiğin ani yükselişlerine (burst) izin verme biçimlerine göre ayrışır.

Algoritma Burst (Patlama) Desteği Çıkış Trafiği Düzgünlüğü Bellek / İşlem Maliyeti Tipik Kullanım Alanı
Fixed Window (Sabit Pencere) Kısmen (Sınırda riskli) Düzensiz Çok düşük (Tek sayaç) Basit kota kontrolleri
Sliding Window Log Yok Çok yüksek Çok yüksek (Her istek saklanır) Düşük trafikli, yüksek hassasiyetli sistemler
Sliding Window Counter Sınırlı Yüksek Düşük (İki sayaç + ağırlık) API ağ geçitleri, mikroservisler
Token Bucket Tam desteklenir Dalgalı Düşük (Sayaç + zaman damgası) Genel amaçlı REST API'ler
Leaky Bucket Yok (Kuyruk ile yumuşatılır) Tamamen sabit Orta (Kuyruk yapısı gerektirir) Veritabanı yazma kuyrukları

Sabit Pencere (Fixed Window) ve Sınır Yoğunluğu Sorunu

Sabit pencere algoritmasında zaman, önceden tanımlanmış sabit aralıklara bölünür (örneğin 12:00:00 - 12:01:00 arası). Her pencere için tek bir sayaç tutulur. İstek geldikçe sayaç artırılır; pencere sınırında sayaç sıfırlanır.

Bu yöntemin kritik zafiyeti pencere sınırındaki trafik yığılmasıdır. Dakikada 100 istek sınırınız olduğunu varsayalım. İstemci 12:00:50 ile 12:01:00 arasında (son 10 saniye) 100 istek gönderirse izin verilir. Saat 12:01:00 olduğunda sayaç sıfırlanır ve istemci 12:01:00 ile 12:01:10 arasında bir 100 istek daha gönderir. Sonuç olarak 20 saniyelik dar bir aralıkta sunucuya 200 istek yönlendirilmiş olur ve bu durum kapasite aşımına yol açabilir.

Kayan Pencere (Sliding Window)

Sabit pencerenin kenar etkisini çözmek için iki varyasyon kullanılır:

  • Sliding Window Log: İstemcinin yaptığı her isteğin zaman damgası (timestamp) bellek içinde (örneğin sıralı bir Redis sorted set veri tipinde) tutulur. Yeni istek geldiğinde, mevcut zamandan belirlenen süre kadar geriye gidilerek o aralık dışındaki tüm kayıtlar silinir. Kalan kayıt sayısı limiti aşıyorsa istek reddedilir. Kesin doğruluk sunar ancak yüksek trafikte bellek tüketimi aşırı yükselir.
  • Sliding Window Counter: Bir önceki pencerenin istek sayısı ile mevcut pencerenin istek sayısını ağırlıklı bir ortalamayla birleştirir. Formül şöyledir:

$$\text{Tahmini İstek} = (\text{Önceki Pencere Sayacı} \times (1 - \text{Geçen Süre Oranı})) + \text{Mevcut Pencere Sayacı}$$

Bellekte yalnızca iki tamsayı tutarak sabit pencereden çok daha pürüzsüz bir sınırlama sunar.

Token Bucket

Endüstride en yaygın benimsenen modellerden biridir. Kapasitesi $B$ olan sanal bir kova düşünün. Bu kovaya saniyede $r$ adet token düzenli olarak doldurulur. Kova dolduğunda taşan token'lar atılır.

Gelen her istek kovadan 1 token tüketir. Kovada yeterli token varsa istek işleme alınır; token bitmişse istek reddedilir. Bu mekanizma, kovanın dolduğu anlarda ani trafik patlamalarına ($B$ kadar eşzamanlı istek) olanak tanırken, uzun vadeli ortalama hızı saniyede $r$ istek düzeyinde tutar.

// Token Bucket Sözde Kodu (Pseudo-code)
class TokenBucket {
    capacity: integer
    refill_rate: float        // Saniye başına eklenen token
    tokens: float
    last_update: timestamp

    function allow_request(tokens_requested = 1) -> boolean {
        now = get_current_timestamp()
        elapsed = now - this.last_update

        // Geçen sürede biriken yeni token'ları hesapla
        this.tokens = min(this.capacity, this.tokens + (elapsed * this.refill_rate))
        this.last_update = now

        if this.tokens >= tokens_requested {
            this.tokens = this.tokens - tokens_requested
            return true
        }
        return false
    }
}

Leaky Bucket

Leaky bucket algoritmasında gelen istekler sabit kapasiteli bir kuyruğa (kovaya) eklenir. Kuyruğun altındaki bir delikten istekler sabit bir çıkış hızıyla ($r$) işlemciye akar. Kuyruk tamamen dolduğunda sisteme ulaşan yeni istekler taşarak doğrudan düşürülür (drop edilir).

Token bucket'tan temel farkı, ani patlamaları doğrudan arka plana iletmemesidir. İstek geliş hızı ne kadar dalgalı olursa olsun, arkadaki servise giden trafik tamamen sabit ve pürüzsüz bir hızda tutulur (traffic shaping).

Limit Nerede Uygulanır?

İstek sınırlama mekanizmasının konumlandırıldığı katman, sistem kaynaklarının ne kadar erken korunacağını belirler.

[İstemci] ──> [Ters Proxy / API Gateway] ──> [Uygulama Katmanı] ──> [Veritabanı]
                      │                              │
                      ▼                              ▼
              [Yerel Bellek / Sayaç]         [Dağıtık Bellek (Redis)]

Ters Proxy ve API Ağ Geçitleri

Uygulama koduna hiç yük bindirmeden istekleri en dış kabukta durdurmak en verimli yaklaşımdır. NGINX üzerinde limit_req direktifi leaky bucket benzeri bir mantıkla çalışır ve belirlenen hızın üzerindeki istekleri geçici bir tamponda tutabilir ya da gecikmesiz reddedebilir. Envoy veya Kong gibi modern API ağ geçitleri ise genellikle token bucket yaklaşımını benimser ve yapılandırmayı yerel bellek ya da dağıtık önbellek üzerinden koordine edebilir.

Uygulama Katmanı ve Dağıtık Sistemlerde Redis

İsteklerin kullanıcı izinlerine, veritabanındaki dinamik abonelik paketlerine veya karmaşık iş mantıklarına göre filtrelenmesi gerektiğinde kontrol uygulama katmanına taşınır.

Birden fazla sunucunun (cluster) hizmet verdiği ortamlarda sunucuların kendi yerel bellekleri üzerinden limit uygulaması tutarsızlık yaratır. İstemci her defasında farklı bir yük dengeleyici düğümüne yönlendirildiğinde toplam limitini fiilen düğüm sayısı kadar katlayabilir. Bu nedenle Redis gibi merkezi, bellek içi (in-memory) veri depoları kullanılır.

Kritik Not: Dağıtık sistemlerde GET ve ardından SET operasyonları yürütmek yarış koşullarına (race condition) neden olur. Eşzamanlı iki istek aynı anda sayacı okuyup 1 artırırsa gerçek istek sayısı doğru kaydedilemez. Çözüm, Redis'in atomik INCR ve EXPIRE komutlarını kullanmak ya da tüm doğrulama mantığını tek bir atomik Redis Lua betiği içerisinde çalıştırmaktır.

Limit Anahtarları (Rate Limit Keys)

Sınırlamanın hangi kritere göre yapılacağı kullanım senaryosuna göre belirlenir:

  • İstemci IP Adresi: Anonim kullanıcılar için geçerlidir. Ancak NAT arkasındaki binlerce kurumsal kullanıcıyı tek bir kullanıcı gibi değerlendirme riski taşır.
  • API Anahtarı veya JWT: Kimliği doğrulanmış kullanıcılar için en adil modeldir. API güvenliği için temel doğrulama yöntemleri ile entegre edildiğinde, her müşterinin kendi kotası üzerinden işlem yapması sağlanır.
  • Uç Nokta (Endpoint) Bazlı: Hafif bir /health ucu saniyede yüzlerce çağrıya izin verirken, karmaşık bir /reports/export ucu dakikada yalnızca 2 çağrı ile sınırlandırılabilir.

HTTP Yanıtları ve İstemci Davranışı

Bir API, rate limit uyguladığında bunu istemciye şeffaf ve anlaşılır HTTP başlıklarıyla bildirmelidir.

HTTP 429 ve HTTP 503 Farkı

  • HTTP 429 Too Many Requests: İstemcinin belirlenen kullanım sınırını aştığını ifade eden doğru durum kodudur. Problem istemcinin gönderim frekansındadır.
  • HTTP 503 Service Unavailable: Sunucunun aşırı yüklenme, bakım veya genel kapasite yetersizliği nedeniyle geçici olarak hizmet veremediğini gösterir. İstemci limitini aşmamış olsa dahi sunucu tarafındaki bir arıza nedeniyle dönebilir.

Retry-After Başlığı

Sunucu 429 yanıtı döndüğünde yanıt başlığına Retry-After eklemelidir. Bu alan iki farklı biçimde verilebilir:

  • Saniye cinsinden tam sayı: Retry-After: 30 (İstemcinin en az 30 saniye beklemesi gerektiğini belirtir).
  • HTTP tarihi (RFC 7231): Retry-After: Wed, 21 Oct 2026 07:28:00 GMT

Üstel Geri Çekilme (Exponential Backoff) ve Jitter

Bir servis 429 döndüğünde, istek yapan istemcilerin tümü aynı anda sabit bir süre (örneğin 5 saniye) bekleyip aynı milisaniyede yeniden deneme yaparsa yeni bir trafik dalgası oluşur. Buna yeniden deneme fırtınası (retry storm) denir.

Bunu önlemek için istemci her başarısız denemede bekleme süresini katlayarak artırmalı (üstel geri çekilme) ve sürenin üzerine rastgele bir sapma değeri eklemelidir:

$$T = (\text{Taban Süre} \times 2^{\text{Deneme Sayısı}}) + \text{Rastgele Sapma (Jitter)}$$

Bu yöntem isteklerin zamana rastgele yayılmasını sağlar ve sunucunun nefes almasına imkan tanır. Otomasyon sistemlerinde ve bot mimarilerinde bu tür gecikme stratejilerinin doğru kurulması operasyon sürekliliği açısından esastır; bu dinamikler güvenli bot kullanımı prensiplerinin de temelini oluşturur.

RateLimit HTTP Başlıkları

Standardizasyon süreçleri IETF bünyesinde devam etmekle birlikte, endüstride yaygın olarak kullanılan başlıklar şunlardır:

  • RateLimit-Limit (veya X-RateLimit-Limit): İzin verilen azami çağrı sayısı.
  • RateLimit-Remaining (veya X-RateLimit-Remaining): Geçerli pencerede kalan kullanılabilir çağrı sayısı.
  • RateLimit-Reset (veya X-RateLimit-Reset): Kotanın sıfırlanacağı zaman damgası (Unix epoch) veya saniye.

Bu başlıklar henüz küresel ve tek tip bir RFC standardı haline gelmediğinden, entegrasyon yapılan servisin dökümantasyonundaki başlık formatı mutlaka doğrulanmalıdır.

Sık Yapılan Hatalar ve Edge Case'ler

Uygulamada sıklıkla gözden kaçan detaylar sistemlerin beklenmedik anlarda kilitlenmesine neden olabilir:

  • NAT ve Paylaşılan Ağ Problemi: İstekleri yalnızca IP adresine göre kısıtlamak, bir üniversite kampüsündeki, kurumsal ofisteki veya ortak mobil baz istasyonundaki tüm kullanıcıların aynı limiti paylaşmasına yol açar. Bir kullanıcının yaptığı aşırı yükleme tüm kurumu bloklayabilir. Benzer şekilde, gelen trafiğin türünü ve topolojisini analiz ederken veri merkezi ve konut proxy farkları göz önünde bulundurulmalı; isteklerin arkasında tekil bir kullanıcının mı yoksa bir proxy ağının mı olduğu doğru ayırt edilmelidir.
  • Ters Proxy Arkasında Yanlış IP Tespiti: Uygulama sunucularınız bir yük dengeleyici veya CDN arkasındaysa, gelen istekteki kaynak IP (remote address) proxy sunucusunun yerel IP'si olarak görünür. Eğer X-Forwarded-For başlığını doğru sanitize etmeden kullanırsanız, tüm dünya tek bir IP adresi üzerinden kısıtlamaya maruz kalır.
  • Saat Senkronizasyonu Hataları: Dağıtık düğümlerde sunucu saatleri (NTP) arasında birkaç yüz milisaniyelik kaymalar olması, pencere sıfırlama anlarında sayaçların senkronizasyonunu bozar.
  • İdempotent Olmayan İsteklerin Yeniden Denenmesi: Limit aşımında istemcilerin POST veya PATCH gibi yan etki oluşturan istekleri denetimsizce yeniden denemesi, veri tabanında mükerrer kayıt oluşumuna zemin hazırlar. Yeniden denemeler yalnızca güvenli ve idempotent (örneğin GET, PUT, DELETE) operasyonlarda veya benzersiz bir işlem anahtarı (idempotency key) ile yürütülmelidir.

Uygulama Kontrol Listesi

Bir API'de istek sınırlama kurgularken aşağıdaki operasyonel adımlar izlenmelidir:

  1. Katman Kararı: Statik varlıklar ve temel kaba kuvvet saldırıları için API Gateway seviyesinde; kullanıcı bazlı iş mantığı için uygulama seviyesinde sınırlama yapın.
  2. Kriter Belirleme: Kimliği doğrulanmış kullanıcılar için API anahtarı veya kullanıcı ID'si, anonim istekler için güvenilirliği doğrulanmış X-Forwarded-For IP adresini anahtar olarak seçin.
  3. Doğru Algoritma Seçimi: Ani patlamaları karşılaması gereken mikroservisler için Token Bucket, katı ve sabit çıkış gerektiren arka plan yazma işlemleri için Leaky Bucket kurgulayın.
  4. Dağıtık Bellek Mimarisi: Çok sunuculu ortamlarda durum bilgisini yerel RAM yerine Redis üzerinde atomik komutlarla (Lua script veya pipeline) saklayın.
  5. Standart Başlık Üretimi: İsteklerin yanıtlarına Retry-After ve kalan limit bilgilerini içeren başlıkları ekleyin.
  6. İstemci Tarafında Geri Çekilme: İstemci yazılımlarınıza Retry-After süresine uyan, üstel geri çekilme ve rastgele jitter içeren yeniden deneme mantıkları ekleyin.

Sıkça Sorulan Sorular

Rate limiting ile throttling arasındaki fark nedir?

Rate limiting, bir zaman penceresinde yapılabilecek toplam istek adedini sınırlar ve sınır aşıldığında çağrıyı HTTP 429 ile anında reddeder. Throttling ise sistem aşırı yüklendiğinde istekleri doğrudan reddetmek yerine geciktirerek, kuyruğa alarak veya bant genişliğini kısarak işlem hızını sunucunun kaldırabileceği bir seviyeye düşürme işlemidir.

HTTP 429 hatası alındığında istemci tarafında ne yapılmalıdır?

İlk olarak yanıt başlığındaki Retry-After parametresi okunmalıdır. Eğer bu başlık varsa, belirtilen süre boyunca servise yeni istek atılmamalıdır. Eşzamanlı çalışan diğer iş parçacıklarının istek hızı düşürülmeli ve başarısız olan istek üstel geri çekilme (exponential backoff) algoritmasıyla yeniden denenmelidir.

Token bucket ve leaky bucket algoritmalarından hangisi tercih edilmelidir?

Uygulamanızın kısa süreli ve meşru trafik patlamalarına (burst) tolerans göstermesi gerekiyorsa Token Bucket algoritması tercih edilmelidir. Ancak arkasındaki veritabanı veya alt sistem kesinlikle ani yükleri kaldıramıyorsa ve trafiğin tamamen sabit, dalgalanmasız bir hızda akması şartsa Leaky Bucket mimarisi kullanılmalıdır.

Retry-After başlığı dönmüyorsa ne kadar süre beklenmelidir?

Sunucu Retry-After başlığı sağlamıyorsa doğrudan sabit bir süre beklenmemelidir. Bunun yerine temel bir bekleme süresi (örneğin 1 saniye) belirlenmeli, her başarısız denemede bu süre üstel olarak artırılmalı ($1, 2, 4, 8\dots$ saniye) ve isteklerin aynı milisaniyede çakışmasını engellemek için üzerine mutlaka rastgele bir gecikme payı (jitter) eklenmelidir.