Rate limiting, API''nizi DDoS''dan, yanlışlıkla oluşan sonsuz döngülerden ve paylaşılan kaynakları tek kullanıcının tüketmesinden korur. Ayrıca paid tier sistemlerde kullanım kotalarını enforcer eder. Bu yazı 4 ana algoritmayı ve Redis ile dağıtık implementasyonunu anlatır.

Fixed Window Counter

İlgili rehberler: REST API güvenlik · GraphQL vs REST · KEYDAL API geliştirme hizmetleri

En basit: her kullanıcı için dakika başı counter tut, limit''i aşınca reddet. Kolaydır ama boundary problemi var — minute sonunda burst yapan kullanıcı 2 dakikanın limitini eriştirebilir.

async function fixedWindow(userId, maxPerMinute = 60) {
    const key = `rl:fixed:${userId}:${Math.floor(Date.now() / 60000)}`;
    const count = await redis.incr(key);
    if (count === 1) await redis.expire(key, 60);
    return count <= maxPerMinute;
}

// Sorun: dakikanın 59. saniyesinde 60 istek, 00. saniyede 60 istek
// → 2 saniye içinde 120 istek geçer

Sliding Window Log

Her isteğin zaman damgasını bir sorted set''te tut; limit kontrolü son 60 saniyede kaç istek var. Tam doğru ama memory maliyeti yüksek (her istek için bir entry).

async function slidingLog(userId, maxPerMinute = 60) {
    const key = `rl:log:${userId}`;
    const now = Date.now();
    const windowStart = now - 60000;

    const pipe = redis.pipeline();
    pipe.zremrangebyscore(key, 0, windowStart);   // eski girişleri sil
    pipe.zcard(key);                               // şu anki count
    pipe.zadd(key, now, `${now}-${Math.random()}`);// yeni giriş
    pipe.expire(key, 60);
    const [, [, count]] = await pipe.exec();
    return count < maxPerMinute;
}

Sliding Window Counter

Fixed window + interpolation. Geçen dakika + bu dakika''nın ağırlıklı ortalaması. Bellek verimli, boundary problemi yok — çoğu production sisteminde kullanılan algoritma.

async function slidingCounter(userId, maxPerMinute = 60) {
    const now = Date.now();
    const currentMin = Math.floor(now / 60000);
    const prevMin = currentMin - 1;
    const percentInCurrentMin = (now % 60000) / 60000;

    const [curCount, prevCount] = await redis.mget(
        `rl:sw:${userId}:${currentMin}`,
        `rl:sw:${userId}:${prevMin}`
    );

    const weighted = (parseInt(prevCount) || 0) * (1 - percentInCurrentMin)
                   + (parseInt(curCount) || 0);
    if (weighted >= maxPerMinute) return false;

    await redis.multi()
        .incr(`rl:sw:${userId}:${currentMin}`)
        .expire(`rl:sw:${userId}:${currentMin}`, 120)
        .exec();
    return true;
}

Token Bucket

Her kullanıcının bir "kovası" var, kapasite N. Saniyede R token eklenir. Her istek 1 token harcar. Kova boşsa reddedilir. Burst''e izin verir ama sürekli rate''e tolerans yok — AWS, Stripe gibi çoğu büyük API bunu kullanır.

-- Atomic token bucket (Redis Lua script)
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])  -- tokens per second
local now = tonumber(ARGV[3])
local cost = tonumber(ARGV[4]) or 1

local bucket = redis.call('HMGET', key, 'tokens', 'last')
local tokens = tonumber(bucket[1]) or capacity
local last = tonumber(bucket[2]) or now

-- Token ekle (elapsed zamana göre)
local elapsed = math.max(0, now - last)
tokens = math.min(capacity, tokens + (elapsed * rate / 1000))

if tokens < cost then
    redis.call('HSET', key, 'tokens', tokens, 'last', now)
    redis.call('EXPIRE', key, 300)
    return 0
end

tokens = tokens - cost
redis.call('HSET', key, 'tokens', tokens, 'last', now)
redis.call('EXPIRE', key, 300)
return 1
const LUA_SCRIPT = `...` // yukarıdaki
const sha = await redis.script('load', LUA_SCRIPT);

async function tokenBucket(userId, capacity = 10, rate = 1) {
    const result = await redis.evalsha(sha, 1,
        `rl:tb:${userId}`, capacity, rate, Date.now());
    return result === 1;
}

Leaky Bucket

Token bucket''un kuzeni — istekler bir kuyruğa girer, belirli rate''te çıkar. Buffered istek kavramı olduğu için burst''u düzleştirir. Genelde traffic shaping için kullanılır.

Hangi Algoritmayı Seçmeli?

AlgoritmaBurstKesinlikMemoryUse Case
Fixed Windowİzin verirDüşükO(1)Basit sistem
Sliding LogHayırTamO(n)Az trafik
Sliding CounterSınırlıYüksekO(1)Genel amaçlı
Token Bucketİzin verirYüksekO(1)Public API
Leaky BucketHayırYüksekO(1)Traffic shaping

Katmanlı Rate Limit

// Express middleware — çok katmanlı
const rateLimit = require('express-rate-limit');

// 1) Global — IP başına
app.use(rateLimit({ windowMs: 60000, max: 300 }));

// 2) Auth — brute force koruması
app.use('/login', rateLimit({ windowMs: 900000, max: 5 }));

// 3) Authenticated user — plan bazlı
app.use('/api', async (req, res, next) => {
    if (!req.user) return next();
    const limits = {
        free:  { rpm: 60,  burst: 10 },
        pro:   { rpm: 300, burst: 50 },
        ent:   { rpm: 3000, burst: 500 }
    };
    const limit = limits[req.user.plan];
    const ok = await tokenBucket(req.user.id, limit.burst, limit.rpm / 60);
    if (!ok) return res.status(429).json({ error: 'Rate limit exceeded' });
    next();
});

Response Headers

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1713398400
Retry-After: 30

# 429 response'unda:
HTTP/1.1 429 Too Many Requests
Retry-After: 15

{
  "error": "rate_limit_exceeded",
  "retryAfter": 15
}

Enterprise: Plan + Quota

Rate limit (saniyelik) + quota (aylık) ikilisi SaaS ürünlerin standart modeli. Rate limit Redis''te, quota PostgreSQL''de (aylık kümülatif counter) tutulur.

API Tasarım Prensipleri ve Güvenli Endpoint Mimarisi

Profesyonel API tasarımı dört unsuru bir araya getirir: doğru protokol seçimi (REST CRUD için ideal, GraphQL flexible queries için, gRPC microservice-to-microservice için), kimlik doğrulama (OAuth 2.0 / OIDC, JWT access token, refresh token rotation), rate limiting (token bucket, sliding window, IP/user/API key bazlı) ve versiyonlama (URL versiyon /v1/, header versiyon, deprecation sürecleri). API endpoint güvenliği için input validation, prepared statement, CORS politikası, idempotency-key (POST için), webhook signature verification ve OpenAPI/Swagger dokümantasyonu modern standartlardır. Yüksek trafikli API'lerde Redis ile rate limit + cache, Kafka veya RabbitMQ ile asenkron iş kuyruğu, OpenTelemetry ile dağıtık izleme önerilir.

API koruma ve rate limiting

Dağıtık rate limit, quota yönetimi ve DDoS koruması için iletişime geçin

WhatsApp