lilydocs
HTTP APIWebSocketConsumer
Güvenlik tarifleri3 dk okuma

Rate limiting

Bağlantı açma ve mesaj gönderme hızını ayrı sınırlayın; Redis kararını WebSocket guard’ına bağlayın.

max_connections eşzamanlı bağlantı kapasitesidir; mesaj kuyruğu sınırı bekleyen işi tutar. Rate limiter ise bir anahtarın belirli sürede kaç işlem yapabildiğini ölçer. Bu üç sınır birbirinin yerine geçmez.

Doğru aşamayı seçin#

Limit Bağlama noktası Reddetme
Bağlantı açma denemesi Handshake middleware 101 öncesinde HTTP 429 ve izinli Retry-After
Açık bağlantının mesaj hızı Message guard WebSocket error, ilişkili ACK veya Close

Handshake anahtarında güvenilir client_ip() veya doğrulanmış policy verisini kullanın. Mesaj anahtarı Principal, tenant, route veya connection'a göre seçilir. 101 sonrasında HTTP 429 dönmez.

Cache bağımlılığı ve config#

İlk uygulamadaki lilyrs feature listesine redis ekleyin ve tokio-util bağımlılığını tanımlayın:

Cargo.toml bağımlılıkları
lilyrs = { version = "0.1.0", features = ["websocket", "redis"] }
tokio-util = "0.7"
Yerel lily.toml
[cache]
mode = "single"
provider = "redis"
redis_url = "redis://127.0.0.1:6379/0"
key_namespace = "chat-api"
operation_timeout_secs = 2

Bu yerel geliştirme config'idir. Gerçek deployment bağlantı ve secret ayarlarını kendi Redis ortamınıza göre kurun. Facade'in redis feature'ı single modunu seçer ve CacheService Singleton'dır. Factory için bunun yerine redis-factory seçilir; named cell resolver kullanılır ve aşağıdaki tek servis örneği uyarlanır. [cache] config başlığı paket adına göre değiştirilmez.

Subject başına mesaj kotası#

Örnek doğrulanmış subject başına 10 saniyede 20 mesajlık fixed window kullanır. src/rate_limit.rs olarak ekleyin; builder'da .guard::<rate_limit::MessageRateLimit>() veya ilgili action/controller'da #[guard(MessageRateLimit)] seçin.

rate_limit.rs
use std::{sync::Arc, time::Duration};
use lilyrs::redis::{CacheOperationContext, CacheService};
use lilyrs::websocket::{ExecutionCancellation, Extensions, WebSocketErrorCode, async_trait};
use lilyrs::websocket::guard::{GuardInitializationError, WebSocketGuardRejection, WsGuard};
use lilyrs::websocket::middleware::WsMessageExchange;
use tokio_util::sync::CancellationToken;

pub struct MessageRateLimit { cache: Arc<CacheService> }
fn reject(code: &'static str, message: &'static str) -> WebSocketGuardRejection {
    WebSocketGuardRejection::error(WebSocketErrorCode::new(code).expect("sabit kod"), message)
        .expect("sabit mesaj")
}
#[async_trait]
impl WsGuard for MessageRateLimit {
    async fn new(extensions: Arc<Extensions>) -> Result<Self, GuardInitializationError> {
        let cache = extensions.get_service::<CacheService>(None).await
            .map_err(|_| GuardInitializationError::MissingDependency)?;
        Ok(Self { cache })
    }
    async fn can_activate(&self, exchange: &mut WsMessageExchange,
        stopping: ExecutionCancellation) -> Result<(), WebSocketGuardRejection>
    {
        let principal = exchange.principal()
            .ok_or_else(|| reject("AUTH_REQUIRED", "Kimlik doğrulaması gerekli."))?;
        let key = format!("ws:chat:write:{}", principal.subject());
        let token = CancellationToken::new();
        let _cancel_on_drop = token.clone().drop_guard();
        let remaining = exchange.deadline().saturating_duration_since(tokio::time::Instant::now());
        let operation = CacheOperationContext::new(token.clone(), remaining.min(Duration::from_secs(1)))
            .map_err(|_| reject("LIMIT_UNAVAILABLE", "İşlem şu an değerlendirilemiyor."))?;
        let work = self.cache.fixed_window_rate_limit_with_context(
            &key, 20, Duration::from_secs(10), &operation,
        );
        tokio::pin!(work);
        let result = tokio::select! {
            biased;
            _ = stopping.cancelled() => { token.cancel(); work.await }
            result = &mut work => result,
        }.map_err(|_| reject("LIMIT_UNAVAILABLE", "İşlem şu an değerlendirilemiyor."))?;
        if result.allowed() { Ok(()) }
        else { Err(reject("RATE_LIMITED", "Çok sık mesaj gönderiyorsunuz.")) }
    }
}

Tam örnek dosyasını indirin.

Anahtar örnektir. Tenant'lar aynı subject'i kullanıyorsa tenant'ı ve limitin paylaşılacağı action grubunu anahtar modeline ekleyin. Fixed window pencere sınırlarında burst davranışı gösterebilir; kota seçimini uygulama yüküne göre yapın.

İptal ve backend hatası#

WS cancellation raw token'ı public değildir. Örnek uygulamaya ait token oluşturur, WS sinyalini gözleyince token'ı cancel eder ve operation future'ını aynı invocation içinde bekler. drop_guard() erken drop'ta token'ı da kapatır. Detached task açılmaz.

Operation süresi kalan mesaj bütçesiyle ve bir saniyelik yerel cap ile sınırlandırılır. Backend hatası sessizce local sayaca dönüşmez; LIMIT_UNAVAILABLE ile reddedilir. Bu davranış dağıtık kota kararının tutarlı kalmasını sağlar; farklı degraded davranış gerekiyorsa ayrıca tasarlayın.

retry_after() değerini istemciye iletmek için açık bir uygulama DTO/yanıt kararı gerekir. Guard ACK rejection'ı keyfi metadata nesnesi kabul etmez. Örnek derlenmiştir; canlı Redis kota yük testi yapılmış kabul edilmemelidir.

Bu sayfanın kaynakları v0.1.0

Anlatım, incelenen framework commit’ine dayanır.

HIZLI BAŞLANGIÇ
Dokümantasyon
Kaynak kod