-- Projeler

IPTV Yansıtma Mimarisi: Throttle, Proxy, TTL Önbellek ve Error Budget

Giriş

Uzun yıllardır IPTV ve medya yansıtma (mirror) sistemleri üzerinde çalışıyorum. Bunların her biri aslında birer veri çekme (scraping) motoru: bir kaynaktan akış ve kanal bilgisini çekip kendi arayüzünüzde sunmak. Yeni başlayanlar “veri çekmek kolay, curl ile at” der. Deneyim der ki; asıl zorluk veriyi çekmek değil, bloklanmadan ve kaynağı düşürmeden bunu ölçekli yapmaktır.

Bu yazıda bir IPTV yansıtma sisteminin mimarisini; proxy rotasyonu, throttle, TLS fingerprint, TTL önbellek ve hata bütçesi (error budget) üzerinden anlatacağım.

1. Throttle: Kaynağın gözünden bakın

İlk ve en çok ihmal edilen kural: kendinizi kaynak sunucunun yerine koyun. Bir kaynaktan 1 saniyede 200 istek geliyorsa, 120. istekten sonra çoğu kaynak sizi ya rate-limit ile karşılar ya da IP bazlı bloklar.

// Basit token bucket throttle
final class Throttle
{
    private array $tokens = [];
    private float $rate;

    public function __construct(float $requestsPerSecond)
    {
        $this->rate = $requestsPerSecond;
    }

    public function wait(string $source): void
    {
        $now = microtime(true);
        $last = $this->tokens[$source] ?? $now;
        $elapsed = $now - $last;
        // izin verilen aralık
        $minInterval = 1.0 / $this->rate;
        if ($elapsed < $minInterval) {
            usleep((int)(($minInterval - $elapsed) * 1_000_000));
        }
        $this->tokens[$source] = microtime(true);
    }
}

Anahtar nokta: throttle’ı kaynak başına tutmaktır. Tek global havuz yanlıştır çünkü farklı kaynakların farklı toleransları vardır.

2. Proxy rotasyonu: Bloklanmadan ölçeklenmek

Tek IP ile rate-limit’i aşamazsınız. İhtiyacınız olan şey sağlıklı bir proxy havuzudur. Havuz yönetiminde en kritik veri yapısı “kara liste + soğuma” dur:

  • Bir proxy 4xx/407 (auth hatası) verdi mi → anında pasif.
  • Ardışık proxy time-out’ları → soğuma süresi koy.
  • Rotasyon rastgele değil, son kullanım zamanına göre sıralı olmalı — aynı proxynin ardışık isteklerde kullanılmaması için.
// Proxy havuzu: başarısız olanları arka plana iterate et
class ProxyPool
{
    private array $proxies = [];
    private array $cooldown = [];

    public function next(): string
    {
        foreach ($this->proxies as $proxy) {
            $cd = $this->cooldown[$proxy] ?? 0;
            if ($cd < time()) {
                return $proxy;
            }
        }
        usleep(100_000);
        return $this->next();
    }

    public function penalize(string $proxy): void
    {
        $this->cooldown[$proxy] = time() + 30;
    }
}

3. TLS/JA3 fingerprint: “Tarayıcı gibi görünmek”

Modern CDN’ler (Cloudflare başta) yalnızca User-Agent’a bakmaz. TLS el sıkışmasının JA3 fingerprint alanına bakar. cURL ile gönderdiğiniz istek tarayıcıdan farklı bir imza taşır ve hemen “bot” olarak sınıflandırılır.

Çözüm seçenekleri:

  • Gerçek bir tarayıcı motoru (Playwright/Puppeteer) ile istek atın.
  • curl-impersonate gibi Chrome imzasını taklit eden araçlar kullanın.
  • TLS fingerprint’i değiştirmek istiyorsanız, HTTP/2 + ALPN + cipher ayarlarını tarayıcı değerlerine eşleyin.

4. TTL önbellek: Aynı stream’i 1000 istekle yeniden çekmeyin

Yansıtma sistemlerinin en büyük israfı: aynı kanal listesini her kullanıcı isteğinde kaynaktan yeniden indirmek. Doğru yaklaşım, sonucu TTL ile bellekte/disk’te tutmak ve kaynağa yalnızca TTL dolunca gitmektir.

$cacheKey = 'xtream:' . md5($sourceUrl);
$ttl = 3600; // 1 saat

$list = apcu_fetch($cacheKey);
if ($list === false) {
    $list = fetchFromSource($sourceUrl);
    apcu_store($cacheKey, $list, $ttl);
}

Bu, kaynak isteklerini %90’dan fazla azaltır hem de yanıtınızı dramatik şekilde hızlandırır. Önbellek tabanları: tek sunucuda APCu, dağıtık yapıda Redis.

5. Hata bütçesi ve yarı-senkrone arka plan

Her kaynak zamanla bozulur. Plan olarak: kullanıcı isteğini asla doğrudan kaynağa bağlamayın. Bunun yerine:

  1. Arka plan bir işçi, kaynağı periyodik çeker ve önbelleğe yazar.
  2. Kullanıcı isteği yalnızca önbelleği okur.
  3. Önbellek boşsa → bir önceki “stale” kopyayı sun, kaynağı async tazele.

Bu, kaynak dayansa bile kullanıcının 200 almasını sağlar. “Stale-while-revalidate” deseni.

Özet: Öncelik sırası

  1. Önce throttle (kaynağı koru).
  2. Sonra TTL önbellek (kaynak isteklerini azalt).
  3. Ardından proxy rotasyonu (ölçeklen).
  4. En sonda fingerprint (bloklanmayı geciktir).

Bu sırayla giderseniz, önce stabilite sonra performans sonra gizlilik kazanırsınız. Tersine giderseniz, fingerprint’i düzeltirken kaynağınızı throttle etmeyi unutup kendinizi bloklatırsınız.