Konuyu Açan
#1
Python Async API Rate Limiting
Giriş ve motivasyon
Birçok API, dakikada belirli bir istek sayısını aştığınızda erişimi kısıtlar veya geçici bloklar uygular. Akışkan bir asenkron uygulamada bu sınırları aşmamak için iki katmanlı bir strateji kullanmak mantıklı: (1) global concurrency kontrol ve (2) per-endpoint veya per-API anahtar düzeyindeRate Limiting. Bu yöntemler, asyncio tabanlı uygulamalarda blokajı minimize ederken, hatalı istek sayısını da azaltır.
Temel kavramlar ve basit uygulama
Kod örneği ve akış
Geliştirme önerileri ve üretimde dikkat edilecek noktalar
İlkeler ve kararlar üzerinden, kendi projenizde hangi seviyede konfigürasyon gerektiğini düşünün. Bu yaklaşımın performans ve güvenilirlik üzerinde nasıl bir etki yarattığı konusunda deneyimleriniz veya karşılaştığınız zorluklar nelerdir?
Güncel API tüketiminde karşılaşılan temel zorluklar arasında rate limitler ve eşzamanlı istek yönetimi öne çıkıyor. Bu yazıda, asenkron Python ile basit, güvenilir ve ölçeklenebilir bir rate limiting yaklaşımasını paylaşacağım.
Giriş ve motivasyon
Birçok API, dakikada belirli bir istek sayısını aştığınızda erişimi kısıtlar veya geçici bloklar uygular. Akışkan bir asenkron uygulamada bu sınırları aşmamak için iki katmanlı bir strateji kullanmak mantıklı: (1) global concurrency kontrol ve (2) per-endpoint veya per-API anahtar düzeyindeRate Limiting. Bu yöntemler, asyncio tabanlı uygulamalarda blokajı minimize ederken, hatalı istek sayısını da azaltır.
Temel kavramlar ve basit uygulama
- Concurrency limiti: aynı anda uç noktalara yapılabilecek maksimum istek sayısını sınırlayan bir yapı kurun. asyncio.Semaphore kullanımı tipiktir. Böylece bir görev, izin almadan yeni bir istek başlatamaz.
- Nöbetleşe bekletme: limit aşıldığında bekleyen istekler için kısa bir “retry-after” stratejisi veya eşzamanlı bekletme (backoff) uygulanır.
- Kuve dengesini koruma: per-API anahtarına göre limitleri izlemek, bir anahtarın aşırı kullanımı nedeniyle diğerlerinden bağımsız olarak davranmayı sağlar.
Kod örneği ve akış
KOD
12345678910111213141516171819202122232425262728293031323334353637383940414243import asyncio
import time
import httpx
class RateLimiter:
def [b]init[/b](self, max_calls: int, period: float):
self.max_calls = max_calls
self.period = period
self.calls = 0
self.lock = asyncio.Lock()
self.reset_task = None
async def acquire(self):
async with self.lock:
if self.calls < self.max_calls:
self.calls += 1
if self.reset_task is None:
self.reset_task = asyncio.create_task(self._reset())
return
# bekle ve tekrar dene
await asyncio.sleep(self.period / self.max_calls)
await self.acquire()
async def _ reset(self):
await asyncio.sleep(self.period)
async with self.lock:
self.calls = 0
self.reset_task = None
async def fetch(api, client, limiter: RateLimiter, url: str):
await limiter.acquire()
resp = await client.get(url)
return resp
async def main():
limiter = RateLimiter(max_calls=60, period=60.0) # 60 çağrı/dk
async with httpx.AsyncClient() as client:
tasks = [fetch(\"myapi\", client, limiter, \"https://api.example.com/data/{}\".format(i)) for i in range(120)]
results = await asyncio.gather(*tasks)
print(len([r for r in results if r.status_code == 200]))
if [b]name[/b] == '[b]main[/b]':
asyncio.run(main())Geliştirme önerileri ve üretimde dikkat edilecek noktalar
- Hatalı yanıt yönetimi: 429 Too Many Requests gelirse, geri dönüş zamanını hesaplayıp exponential backoff uygulayın; header’da retry-after varsa ona uyun.
- Per-key rate limiting: API anahtarı veya kullanıcıya göre ayrı limiterlar kullanın; bir anahtarın limitleri aşması diğerlerini etkilemesin.
- Dediğiniz gibi asenkron I/O’da blokajı azaltmak için, CPU-bound işlemleri threadpool ile ayırın; IO-bound kısımları asenkron tutun.
- İzleme ve gözlem: istek başına gecikme ve başarısızlık oranlarını kaydedin; grafiklerle tespit edin hangi endpointlerin limitlere yaklaştığını görün.
- Test stratejisi: sahte zamanlayıcılar veya simülatörlerle limitlerin davranışını test edin; özellikle backoff’un doğru çalıştığından emin olun.
İlkeler ve kararlar üzerinden, kendi projenizde hangi seviyede konfigürasyon gerektiğini düşünün. Bu yaklaşımın performans ve güvenilirlik üzerinde nasıl bir etki yarattığı konusunda deneyimleriniz veya karşılaştığınız zorluklar nelerdir?
