Rate limiting
Redis’in atomik sayaç işlemini bir HTTP guard’a bağlayarak kota ve Retry-After uygulayın.
HTTP rate-limit key’i, kota, pencere ve hata politikası uygulamanıza aittir. lilyrs::redis bu politika için Redis üzerinde atomik fixed-window işlemi sağlar. Bu rehberdeki ApiRateLimitGuard uygulama kodudur.
Bağımlılıklar ve Redis#
Çalışan bir Redis servisi gerekir. Mevcut lilyrs satırına redis feature’ını ve örneğin kullandığı tokio bağımlılığını ekleyin:
lilyrs = { version = "0.1.0", features = ["http-api", "redis"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }[cache]
mode = "single"
provider = "redis"
redis_url = "redis://127.0.0.1:6379/0"
key_namespace = "users-api"
operation_timeout_secs = 2redis feature’ı single modu açar. Cache servisi DI üzerinden configuration ve bağlantı yaşam döngüsünü yönetir. Paket adı lily_redis, facade modülü lilyrs::redis olsa da configuration bölümü [cache] olarak kalır.
IP başına kota guard’ı#
use std::{sync::Arc, time::Duration};
use lilyrs::redis::CacheService;
use lilyrs::http_api::{
CancellationToken, ExecutionCancellation, Extensions, GuardInitError,
GuardRejection, GuardTrait, HttpErrorCode, Request,
};
pub struct ApiRateLimitGuard { cache: Arc<CacheService> }
fn unavailable() -> GuardRejection {
GuardRejection::service_unavailable(HttpErrorCode::new("RATE_LIMIT_UNAVAILABLE").unwrap()).unwrap()
}
#[lilyrs::http_api::async_trait::async_trait]
impl GuardTrait for ApiRateLimitGuard {
async fn new(extensions: Arc<Extensions>) -> Result<Self, GuardInitError> {
let cache = extensions.get_service::<CacheService>(None).await
.map_err(|_| GuardInitError::Dependency {
guard: "ApiRateLimitGuard", reason: "Redis cache unavailable".into(),
})?;
Ok(Self { cache })
}
async fn can_activate(&self, request: &mut Request, stopping: ExecutionCancellation)
-> Result<(), GuardRejection>
{
let ip = request.client_ip().ok_or_else(unavailable)?;
// Bu politika, seçili endpoint grubunda IP başına 60 saniyede 100 istektir.
let key = format!("http:public-api:{ip}");
let token = CancellationToken::new();
let operation = self.cache.operation_context(token.clone()).map_err(|_| unavailable())?;
let work = self.cache.fixed_window_rate_limit_with_context(
&key, 100, Duration::from_secs(60), &operation,
);
tokio::pin!(work);
let decision = tokio::select! {
biased;
_ = stopping.cancelled() => { token.cancel(); work.await }
result = &mut work => result,
}.map_err(|_| unavailable())?;
if decision.allowed() { return Ok(()); }
Err(GuardRejection::too_many_requests(
HttpErrorCode::new("RATE_LIMITED").unwrap(), decision.retry_after(),
).map_err(|_| unavailable())?)
}
}Korunacak action’lara #[guard(ApiRateLimitGuard)] ekleyin. Bu örnek, o endpoint grubunda IP başına 60 saniyede 100 isteği paylaşır. Kota aşılırsa 429 ve backend kararındaki Retry-After, Redis kullanılamıyorsa 503 döner.
Key seçimi#
Kullanıcı başına limit için önce AuthGuard çalıştırıp key’i doğrulanmış principal’dan üretin. IP bazlı limit NAT arkasındaki kullanıcıları ortak sayabilir. Reverse proxy varsa trusted proxy config doğru olmalıdır.
Fixed-window politikası pencere sınırında burst davranışı gösterebilir. Bu davranışı kota seçiminde hesaba katın. max_in_flight_requests eşzamanlı kapasite sınırıdır; zaman başına rate limit değildir.
Cancellation bağlantısı#
HTTP ExecutionCancellation, Redis CancellationToken tipine doğrudan çevrilmez. Örnek ayrı token kurar; durdurma sinyalinde token’ı cancel edip aynı işi await eder. Kopuk bir cancellation task’ı bırakmaz.
Daha kısa ICache::fixed_window_rate_limit(...) yolu configured operation deadline kullanır, fakat HTTP cancellation’ını kendiliğinden bağlamaz.
Bu sayfanın kaynakları v0.1.0
Anlatım, incelenen framework commit’ine dayanır.