Hız sınırlama (rate limiting), diğer adıyla request throttling, bir kullanıcının veya servisin bir API'ye ya da sunucuya hangi hızda istek gönderebileceğini denetleyen; kötüye kullanımı önleyen, kaynakların adil dağıtımını sağlayan ve sistem kararlılığını koruyan bir mekanizmadır.
Hız sınırlamayı anlamak
Hız sınırlama, performans düşüşüne, hizmet reddi (DoS) saldırılarına veya kaynak tükenmesine yol açabilecek aşırı istek hacimlerine karşı servisleri korur. Sistem kaynaklarının tüm meşru kullanıcılara açık kalmasını sağlar ve tek bir tarafın erişimi tekeline almasını engeller. Bir proxy servisi bu limitlerin uygulanmasında çoğu zaman kritik rol oynar: istemci isteklerine upstream servislere ulaşmadan önce limit uygular ya da upstream limitlerini istemcilere geri bildirir.
Hız sınırlama neden uygulanır?
- Kaynak koruması: sunucuların aşırı istekle boğulmasını engeller; CPU, bellek ve ağ bant genişliğini korur.
- Kötüye kullanımın önlenmesi: istek denemelerini sınırlayarak kaba kuvvet saldırılarını, credential stuffing'i ve diğer kötü niyetli faaliyetleri azaltır.
- Adil kullanım: tüm istemcilerin paylaşılan kaynaklara eşit erişmesini sağlar, tek bir istemcinin sistemi tekeline almasını önler.
- Maliyet denetimi: kullanım bazlı ücretlendirilen servislerde kaynak tüketimine tavan koyarak işletme maliyetlerini kontrol altında tutar.
Yaygın hız sınırlama algoritmaları
Limitleri izlemek ve uygulamak için farklı algoritmalar kullanılır; her biri ani yük artışlarını (burst) ele alma biçimi ve kaynak kullanımı bakımından farklı özellikler taşır.
Token Bucket (jeton kovası)
Token Bucket algoritması, sabit hızda jetonla dolan, sabit kapasiteli bir kovayı modeller. Her istek bir jeton tüketir. Kova boşsa istek reddedilir veya kuyruğa alınır. Bu algoritma belirli ölçüde burst'e izin verir: istekler, kovanın kapasitesine kadar mevcut jetonların birden fazlasını tüketebilir.
Leaky Bucket (sızdıran kova)
Leaky Bucket algoritması istekleri sabit bir çıkış hızında işler. İstekler bir kuyruğa ("kova") eklenir. Kuyruk doluysa yeni istekler reddedilir. İstekler kovadan sabit hızda "sızar" ve böylece istikrarlı bir işleme akışı sağlanır. Bu algoritma burst'leri yumuşatır, ancak kuyrukta beklemek zorunda kalan istekler için gecikme yaratır.
Fixed Window Counter (sabit pencere sayacı)
Fixed Window Counter algoritmasında bir zaman penceresi (örneğin 60 saniye) tanımlanır ve bir sayaç bu pencere içindeki istekleri izler. Pencere dolduğunda sayaç sıfırlanır. Pencere içinde limiti aşan istekler reddedilir. Dezavvantajı, pencere sınırlarındaki "burst sorunu"dur: istemciler ardışık iki pencere boyunca izin verilenin iki katı istek gönderebilir.
Sliding Window Log (kayan pencere günlüğü)
Sliding Window Log algoritması her istek için bir zaman damgası kaydeder. Yeni bir istek geldiğinde sistem son N saniyedeki (pencere) zaman damgalarının sayısını hesaplar. Bu sayı limiti aşarsa istek reddedilir. Yöntem doğrudur, ancak tüm zaman damgalarını sakladığı için bellek açısından maliyetli olabilir.
Sliding Window Counter (kayan pencere sayacı)
Bu algoritma, her isteği günlüğe yazmanın bellek yükünü üstlenmeden sınır sorununu azaltmak için Fixed Window ile Sliding Window Log yaklaşımlarını birleştirir. İki sabit pencere kullanır: geçerli pencere ve önceki pencere. Geçerli isteğin zaman damgası, isteğin geçerli pencere içindeki konumunu belirler. İzin verilen istek sayısı, geçerli pencerenin geçen kısmına göre önceki pencerenin sayımı ile geçerli pencerenin sayımının ağırlıklı ortalaması olarak hesaplanır.
Algoritma karşılaştırması
| Özellik | Token Bucket | Leaky Bucket | Fixed Window Counter | Sliding Window Log | Sliding Window Counter |
|---|---|---|---|---|---|
| Burst yönetimi | Burst'e izin verir | Burst'ü yumuşatır | Burst'e açık | Burst'ü iyi yönetir | Burst'ü iyi yönetir |
| Kaynak kullanımı | Orta | Orta | Düşük | Yüksek (bellek) | Düşük–orta |
| Karmaşıklık | Orta | Orta | Düşük | Yüksek | Orta |
| Doğruluk | İyi | İyi | Zayıf (sınır durumu) | Yüksek | İyi |
| Gecikme etkisi | Düşük (jeton varsa) | Yüksek (kuyruklama) | Düşük | Düşük | Düşük |
Hız sınırlamayı tespit etmek
Bir hız limiti aşıldığında API veya servis genellikle belirli HTTP durum kodları ve başlıklarıyla yanıt verir.
HTTP durum kodu 429 Too Many Requests
Hız sınırlama için standart HTTP durum kodu 429 Too Many Requests'tir. Bu kod, kullanıcının belirli bir süre içinde çok fazla istek gönderdiğini belirtir.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{
"error": "Rate limit exceeded. Try again in 30 seconds."
}
Yanıt başlıkları
API'ler limit durumu ve nasıl davranılacağı hakkında bağlam sunan başlıkları çoğu zaman yanıta ekler.
Retry-After: (RFC 7231, Bölüm 7.1.3) user agent'ın yeni bir istek göndermeden önce ne kadar beklemesi gerektiğini belirtir. Değeri saniye cinsinden bir tam sayı ya da belirli bir tarih/saat olabilir.X-RateLimit-Limit: geçerli hız limiti penceresinde izin verilen azami istek sayısı.X-RateLimit-Remaining: geçerli hız limiti penceresinde kalan istek sayısı.X-RateLimit-Reset: geçerli hız limiti penceresinin sıfırlanacağı zaman (genellikle Unix epoch saniyesi).
Bu X-RateLimit-* başlıkları yaygındır, ancak herhangi bir RFC ile standartlaştırılmamıştır; adlandırmaları ve davranışları servisten servise değişebilir.
Hız limitleriyle başa çıkmak
Harici servislerle çalışan sağlam uygulamalar geliştirmek için limitlerin istemci tarafında doğru ele alınması şarttır.
Jitter'lı üstel geri çekilme (exponential backoff)
Bu, hız sınırlaması dahil başarısız isteklerin yeniden denenmesi için standart stratejidir.
- Üstel geri çekilme: istemci, denemeler arasında üstel olarak artan bir süre bekler (örneğin 1 saniye, sonra 2 saniye, sonra 4 saniye, sonra 8 saniye).
- Jitter: bekleme süresine küçük ve rastgele bir gecikme eklenir. Böylece limit sıfırlandıktan sonra tüm istemcilerin aynı anda yeniden denemesi ve yeni bir sınırlama dalgası tetiklemesi önlenir.
import time
import random
import requests
def make_request_with_retry(url, max_retries=5):
retries = 0
while retries < max_retries:
try:
response = requests.get(url)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 1))
print(f"Rate limited. Retrying after {retry_after} seconds.")
time.sleep(retry_after)
elif 200 <= response.status_code < 300:
return response
else:
response.raise_for_status() # Diğer HTTP hataları için istisna fırlat
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
# Jitter'lı üstel geri çekilme
delay = (2 ** retries) + random.uniform(0, 1) # 2^retries + 0 ile 1 arasında rastgele ondalık
print(f"Retrying in {delay:.2f} seconds...")
time.sleep(delay)
retries += 1
raise Exception(f"Failed to make request after {max_retries} retries.")
# Örnek kullanım:
# response = make_request_with_retry("https://api.example.com/data")
# if response:
# print("Request successful:", response.json())
Retry-After başlığına uyun
API bir Retry-After başlığı gönderiyorsa istemciler bu yönergeye uymak zorundadır. Değer, aynı endpoint'e yeni bir istek göndermeden önce beklenmesi gereken asgari süreyi belirtir.
İstemci tarafında önbellekleme
Sık erişilen ve nadiren değişen endpoint'lerin yanıtlarını önbelleğe alın. Bu, API'ye gönderilen istek sayısını azaltır ve dolaylı olarak limitler içinde kalmanıza yardımcı olur.
İstekleri gruplamak
API destekliyorsa birden fazla küçük işlemi tek bir büyük istekte birleştirin. Bu, toplam API çağrısı sayısını azaltır.
Öngörülü throttling
İstemciler kendi istek hızlarını izleyip bilinen limitlere yaklaştıkça 429 yanıtını beklemeden proaktif olarak yavaşlayabilir veya duraklayabilir. Bunun için API'nin hız limitlerinin önceden bilinmesi gerekir.
Hız sınırlama için proxy servisi yapılandırması
Sağlam bir proxy servisi, hem proxy üzerinden servislere erişen istemciler hem de proxy'nin upstream API'lerle kendi etkileşimleri için limit yönetimine dair kapsamlı özellikler sunar.
Gelen trafiğe limit uygulama
Proxy, gelen istemci isteklerine çeşitli ölçütlere göre hız limiti uygulayabilir.
- İstemci IP adresi: tek bir IP'den gelen istekleri sınırlar.
- API anahtarı/token: belirli bir kimlik doğrulama bilgisiyle ilişkili istekleri sınırlar.
- Kullanıcı kimliği: proxy, başlıklardan veya token'lardan kullanıcı bilgisini çıkarabiliyorsa kullanılır.
- Yol/endpoint: farklı API endpoint'leri için farklı limitler (örneğin
/searchlimiti/admin/deletelimitinden yüksek olabilir).
# Örnek: IP bazlı hız sınırlama için proxy yapılandırması
http:
routers:
api-router:
rule: "Host(`api.example.com`)"
service: api-service
middlewares: [rate-limit-ip]
middlewares:
rate-limit-ip:
rateLimit:
average: 100 # saniyedeki istek sayısı
burst: 50 # ortalamanın üzerindeki azami burst
sourceCriterion: "ipStrategy" # limiti kaynak IP başına uygula
Upstream servislere giden trafiğin yönetimi
Proxy'nin kendisi upstream API'leri tükettiğinde, bu harici servisleri boğmamak için kendi hız sınırlamasını uygulayabilir. Proxy'nin birden çok kaynaktan veri topladığı entegrasyon senaryolarında bu kritiktir.
- Upstream'e özel limitler: proxy'nin iletişim kurduğu her upstream servis için ayrı limitler tanımlayın.
- Circuit breaking: bir upstream servis yanıt vermez hale geldiğinde ya da proxy'yi sürekli sınırladığında arızayı izole etmek için hız sınırlamayı circuit breaker desenleriyle birleştirin.
Özelleştirme ve ayrıntı düzeyi
Gelişmiş proxy yapılandırmaları hız sınırlama üzerinde ince ayar imkânı verir:
- Dinamik limitler: limitleri backend sağlığına, günün saatine veya diğer operasyonel metriklere göre ayarlayın.
- Kademeli limitler: farklı istemci kademeleri için farklı limitler uygulayın (örneğin ücretsiz ve premium kullanıcılar).
- Kota yönetimi: kısa vadeli limitlerin yanı sıra daha uzun vadeli kotalara göre de kullanımı izleyin (örneğin aylık istek sayısı).
İzleme ve uyarı
Bir proxy servisi hız limiti istatistiklerini izlemek için araçlar sunmalıdır:
- İstek sayıları: toplam istekleri, başarılı istekleri ve limite takılan istekleri izleyin.
- Limit ihlalleri: belirli istemciler veya upstream servisler için limitlere yaklaşıldığında ya da limitler aşıldığında uyarı üretin.
- Kullanım eğilimleri: zaman içindeki istek desenlerini görselleştirerek olası darboğazları veya kötüye kullanımı tespit edin.
İzleme, operasyon ekiplerinin trafik desenlerini anlamasına, limit yapılandırmalarını iyileştirmesine ve sorunları servis erişilebilirliğini etkilemeden önce proaktif biçimde çözmesine yardımcı olur.
