← Bloga geri dön

Birden Fazla Sunucuda Dağıtılmış Parser: Çatışma ve Ban Olmadan Proxy Havuzunu Bölme

Dağıtık ayrıştırıcı mimarisini birden fazla sunucuda inceliyoruz - proxy havuzunu nasıl doğru bir şekilde bölüştüreceğiz, bir IP'den gelen isteklerin tekrarlanmasını nasıl önleyeceğiz ve yasaklamaları nasıl en aza indireceğiz.

📅21 Ekim 2026

Ayrıştırıcı bir sunucudan birden fazla işçi içeren bir kümeye geçtiğinde, yeni bir sorun ortaya çıkar - birden fazla makine aynı proxy havuzunu aynı anda kullanmaya başlar. Koordinasyon olmadan, bu durum isteklerde IP'nin tekrarına, bir adres üzerindeki limitlerin aşılmasına ve toplu yasaklamalara yol açar. Bu makalede, sunucular arasında proxy'leri nasıl doğru bir şekilde parçalayacağımızı, hangi dağıtım şemalarının en iyi çalıştığını ve bunu Python ve Redis ile pratikte nasıl uygulayacağımızı inceleyeceğiz.

Dağıtık ayrıştırıcıda proxy'lerin neden çakıştığı

Tipik bir durumu hayal edelim: 500 proxy'den oluşan bir havuzunuz var ve her biri 50 paralel ayrıştırıcı işçisi çalıştıran üç sunucunuz var. Her sunucu, koordinasyon olmadan genel listeden proxy alıyorsa, farklı makinelerdeki iki işçi aynı IP'den aynı alan adına istek gönderebilir. Anti-bot sistemleri için bu, bir adresten gelen isteklerin anormal derecede yüksek bir sıklıkta olduğu gibi görünür - ve IP birkaç dakika içinde yasaklanır.

İkinci sorun - yük dağılımının dengesizliği. Eğer proxy'ler sadece "nasıl olursa" şeklinde paylaşılıyorsa, bir sunucu en hızlı ve yeni IP'leri alabilirken, diğeri kötüleşmiş bir üne sahip olanları alabilir. Sonuç olarak, sunuculardaki ayrıştırma hızı kat kat farklılık gösterecek ve hata oranı da öyle.

Üçüncü sorun - durum kaybı. Eğer bir proxy bir sunucuda yasaklanmışsa, diğer sunucular bundan haberdar olmayacak ve kullanmaya devam edecek, ta ki onlar da yasaklanana kadar. Merkezi bir proxy durum kayıt sistemi olmadan, her sunucu "Amerika'yı yeniden keşfediyor" ve istekleri boşa harcıyor.

Üç sorunun çözümü, tek bir koordinasyon mekanizması getirmektir: ya merkezi bir durum deposu (Redis, PostgreSQL, Consul) aracılığıyla ya da sunucular arasında aralıkların kesişmediği önceden belirlenmiş bir bölme ile.

Dağıtık ayrıştırıcının mimarisi

Proxy'leri bölmeden önce, kümenin genel mimarisi ile ilgili karar vermek gerekir. İki temel yaklaşım vardır.

Statik şema (shared-nothing). Proxy havuzu, sunucular arasında önceden, bir kez başlatıldığında bölünür. Her sunucu kendi kesişmeyen segmentini alır ve diğer sunuculara başvurmadan bağımsız olarak çalışır. Artı - maksimum basitlik ve hata toleransı: bir sunucunun düşmesi diğerlerini etkilemez. Eksi - eğer bir sunucuda proxy'lerin yarısı yasaklanırsa, komşusundan "ödünç" alamaz.

Dinamık şema (shared-state). Tüm sunucular, her proxy'nin durumunu saklayan ve istek üzerine kilitleme ile veren ortak bir depoya (genellikle Redis) başvurur. Artı - havuzun eşit kullanımı ve yasaklara anında tepki. Eksi - depo, tek bir hata noktası haline gelir, erişilemezlik durumu için ayrı bir mantık gerektirir.

Pratikte, pazar yerleri ve sosyal medya için genellikle bir hibrit tercih edilir: Redis üzerindeki broker, proxy'leri kısa bir süreliğine (TTL) kilitleyerek verir ve Redis erişilemez olduğunda sunucular geçici olarak yerel bir yedek havuza geçer. Bu yaklaşım, statik şemanın güvenilirliğini dinamik şemanın esnekliği ile birleştirir.

Sunucular arasında proxy havuzunu bölme yöntemleri

Havuzu bölmek için uygulanabilecek belirli algoritmaları inceleyelim.

Modül ile basit bölme (round-robin sharding). Proxy'leri 0'dan N-1'e kadar numaralandırdık, i numaralı sunucu, index % sunucu_sayısı == i olan tüm proxy'leri alır. Basit, ama sunucu ekleme veya kaldırma durumunda kötü bir şekilde dayanır - makinelerin sayısı değiştiğinde, neredeyse tüm havuz yeniden dağıtılır.

Tutarlı hashleme. Her sunucuya ve her proxy'ye hash halkasında bir pozisyon atanır (genellikle IP adresinin veya sunucu ID'sinin hash'i ile). Proxy, halka üzerindeki en yakın sunucuya bağlanır. Sunucu eklenip çıkarıldığında, yalnızca küçük bir kısmı yeniden dağıtılır, tüm havuz değil - bu, dinamik olarak ölçeklenen kümeler için ana avantajdır (örneğin, otomatik ölçeklendirme ile Kubernetes).

Merkezi broker ile kilitleme. Proxy'leri sunucuya sıkı bir şekilde bağlamak yerine, tüm sunucular, geçici bir kilitleme ile "boş" bir proxy talep eden ortak bir hizmete (Redis, RabbitMQ) başvurur. Kullanım sonrası proxy havuza geri döner. Bu, özellikle sunuculardaki yük dengesiz olduğunda en esnek seçenektir.

Alanlara veya görevlere göre bölme. Eğer ayrıştırıcı birden fazla kaynaktan (örneğin, Wildberries, Ozon ve Avito'yu paralel olarak) çalışıyorsa, sadece proxy'leri sunucular arasında değil, kaynaklar arasında da bölmek mantıklıdır - böylece aynı IP birden fazla platformda görünmez. Bu, IP'nin, böyle platformlar veri alışverişinde bulunduğunda, genel bir kara listeye düşme riskini azaltır.

Tablo: Proxy shardlama yöntemlerinin karşılaştırması

Yöntem Zorluk Ölçeklenebilirliğe dayanıklılık Ne zaman uygulanmalı
Modül ile bölme Düşük Kötü Sabit sunucu sayısı
Tutarlı hashleme Orta İyi Küme otomatik ölçeklendirme
Merkezi broker Orta-yüksek Mükemmel Dengesiz yük, yüksek trafik
Alanlara göre bölme Düşük Orta Birden fazla platformu aynı anda ayrıştırma

Redis üzerinde proxy aracısının uygulanması

Çoğu dağıtık ayrıştırıcı için en pratik seçenek, Redis üzerinde merkezi bir broker'dır. Redis, hızlı olması, atomik işlemleri desteklemesi ve TTL'yi "kutudan çıkarır çıkarmaz" sunması nedeniyle uygundur. Fikir basit: Tüm mevcut proxy'ler Redis Set içinde yer alır ve belirli bir işçiye proxy verildiğinde, belirli bir süre için TTL ile kilitlenir.

Aşağıda, redis-py kütüphanesi ile Python'da çalışan bir broker örneği bulunmaktadır. Bu, kilitleme ile boş proxy verme, kullanım sonrası serbest bırakma ve proxy'yi yasaklı olarak işaretleme işlevlerini gerçekleştirir.

import redis
import time
import random

r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)

PROXY_POOL_KEY = "proxy:pool"       # tüm çalışan proxy'lerin kümesi
LOCK_PREFIX = "proxy:lock:"         # belirli bir proxy için kilit ön eki
BANNED_PREFIX = "proxy:banned:"     # yasaklama işareti ön eki
LOCK_TTL = 30                       # proxy'yi ne kadar süreyle kilitliyoruz
BAN_TTL = 3600                      # proxy'nin yasaklı olarak kabul edildiği süre

def load_pool(proxy_list):
    """Küme başlatıldığında bir kez proxy listesini Redis'e yükleriz."""
    if proxy_list:
        r.sadd(PROXY_POOL_KEY, *proxy_list)

def get_free_proxy(server_id, max_attempts=20):
    """Her sunucu bu fonksiyonu çağırarak boş bir proxy alır."""
    candidates = list(r.smembers(PROXY_POOL_KEY))
    random.shuffle(candidates)

    for proxy in candidates[:max_attempts]:
        if r.exists(BANNED_PREFIX + proxy):
            continue
        lock_key = LOCK_PREFIX + proxy
        # proxy boşsa atomik kilitleme ayarı
        acquired = r.set(lock_key, server_id, nx=True, ex=LOCK_TTL)
        if acquired:
            return proxy
    return None  # boş proxy bulunamadı, yedek gerekli

def release_proxy(proxy):
    """Başarılı bir istek sonrası proxy'yi serbest bırakırız."""
    r.delete(LOCK_PREFIX + proxy)

def mark_banned(proxy):
    """Proxy'yi BAN_TTL saniye boyunca yasaklı olarak işaretleriz, kilidi kaldırırız."""
    r.set(BANNED_PREFIX + proxy, 1, ex=BAN_TTL)
    r.delete(LOCK_PREFIX + proxy)
    r.srem(PROXY_POOL_KEY, proxy)  # geçici olarak havuzdan çıkarırız

def return_to_pool_if_recovered():
    """Periyodik görev: yasak süresi dolan proxy'leri geri döndürürüz."""
    for proxy in r.scan_iter(match=BANNED_PREFIX + "*"):
        clean_proxy = proxy.replace(BANNED_PREFIX, "")
        if not r.exists(BANNED_PREFIX + clean_proxy):
            r.sadd(PROXY_POOL_KEY, clean_proxy)

Buradaki ana nokta, Redis'te SET ... NX EX kullanımının olmasıdır. Bu komut atomiktir: Eğer iki sunucu aynı anda aynı proxy'yi kilitlemeye çalışırsa, yalnızca biri başarılı olur, diğeri başarısız olur ve listedeki bir sonraki adaya geçer. Bu, ortak havuza paralel erişim sırasında sunucular arasında çakışmaları önler.

Broker'ın bir ayrıştırıcı işçisinde kullanımı örneği:

import requests

def fetch_with_proxy(url, server_id):
    proxy = get_free_proxy(server_id)
    if not proxy:
        raise RuntimeError("Havuzda boş proxy yok")

    proxies = {"http": f"http://{proxy}", "https": f"http://{proxy}"}
    try:
        response = requests.get(url, proxies=proxies, timeout=10)
        if response.status_code in (403, 429):
            mark_banned(proxy)
        else:
            release_proxy(proxy)
        return response
    except requests.exceptions.RequestException:
        mark_banned(proxy)
        raise

Bu şema kolayca ölçeklenebilir - 10 veya 50 sunucu ekleyebilirsiniz, hepsi aynı Redis örneğine başvuracak ve IP'ler arasında çakışma olmayacak, çünkü kilitleme depolama düzeyinde atomiktir, ayrı bir sunucu düzeyinde değil.

Yapışkan oturumlar ve proxy'lerin görevlere bağlanması

Bazı görevler için "herhangi bir boş proxy" vermek yeterli değildir. Eğer ayrıştırıcı kullanıcı davranışını taklit ediyorsa - örneğin, ürün kartlarını sırasıyla gözden geçiriyorsa, sepete ekliyorsa, katalog sayfalarına geçiyorsa - tüm bir oturumun isteklerinin aynı IP'den gelmesi önemlidir. Aksi takdirde, site oturum ortasında coğrafi veya cihaz değişikliğini görecek ve bunu şüpheli olarak engelleyecektir.

Bunun için yapışkan oturum kavramı getirilir: proxy, yalnızca bir isteğe değil, belirli bir göreve veya kullanıcı oturumuna bağlıdır. Redis'te bu, session:{task_id} -> proxy şeklinde bir anahtar ile TTL olarak oturumun beklenen süresine eşit bir süreyle uygulanır.

SESSION_PREFIX = "proxy:session:"
SESSION_TTL = 600  # Oturum için 10 dakika

def get_sticky_proxy(task_id, server_id):
    existing = r.get(SESSION_PREFIX + task_id)
    if existing:
        return existing
    proxy = get_free_proxy(server_id)
    if proxy:
        r.set(SESSION_PREFIX + task_id, proxy, ex=SESSION_TTL)
    return proxy

Uzun oturum gerektiren görevler için - örneğin, gerçek bir alıcının davranışını taklit eden Wildberries katalogunu dolaşmak - mümkün olduğunca normal bir kullanıcı gibi davranan proxy'leri kullanmak kritik öneme sahiptir. Burada, gerçek ev IP'lerine bağlı olan ve veri merkezi adreslerine göre pazar yerlerinin anti-bot sistemlerinde daha az şüphe uyandıran rezidans proxy'leri iyi bir seçimdir.

Yasakların işlenmesi ve kümedeki döngü

Dağıtık bir sistemde, proxy'nin yasak bilgisi anında tüm sunuculara ulaşmalıdır, yalnızca bu yasaklamayı alan sunucuya değil. Yukarıdaki örnekte, bu işlev mark_banned ile sağlanır - bir sunucu hedef siteden 403 veya 429 aldığında, proxy Redis'teki genel havuzdan çıkarılır ve diğer sunucular otomatik olarak onu almaktan vazgeçer.

Yasakları belirlemek için yalnızca HTTP yanıt kodunu değil, aynı zamanda sayfanın içeriğini de analiz etmek önemlidir - bazı siteler 200 OK döndürür, ancak gerçek içeriğin yerine CAPTCHA veya engelleme ile döner. Yasak tespit mantığı genellikle şöyle görünür:

  • HTTP kodları 403, 429, 503 - engelleme veya hız sınırlaması belirtisidir.
  • Captcha veya kimlik doğrulama sayfasına yönlendirme - isteğin ardından URL'yi kontrol etme.
  • Yanıt boyutunda ani bir azalma veya beklenen CSS seçicilerinin olmaması - engelleme belirtisidir.
  • Bağlantı zaman aşımı - genellikle proxy'nin tamamen yanıt vermediğini gösterir.

Yasak kaydedildikten sonra, proxy'yi havuzdan sonsuza dek silmek yerine, onu TTL ile "karantinaya" almak mantıklıdır (örneğin - BAN_TTL), bu süre dolduğunda proxy otomatik olarak havuza geri döner. Pazar yerlerinde ve sosyal medyada birçok yasak geçicidir ve 30-60 dakika içinde kaldırılır.

Eğer yasaklar, yükün eşit dağıtılmasına rağmen çok sık oluyorsa, bu genellikle proxy havuzunun kalitesinin düşük olduğu veya gerekli istek hacmi için yetersiz boyutta olduğu anlamına gelir. Bu tür durumlarda, veri merkezi proxy'leri ile havuzu genişletmek (örneğin, sayfaların erişilebilirliğini kontrol etmek için) ve daha pahalı rezidans IP'lerini yalnızca kritik oturumlar için kullanmak, tüm trafik için harcamaları artırmadan yasak oranını düşürmeye yardımcı olur.

Proxy havuzunun sağlık izleme

Birden fazla sunucudan oluşan bir kümede, genel resmi kaybetmek kolaydır: şu anda kaç proxy aktif, kaç yasaklı, her birinin ortalama yanıt hızı nedir. İzleme olmadan, ayrıştırmanın neden yavaşladığını anlamak zordur - bir sunucu mu düştü, yoksa canlı proxy'ler mi tükendi.

Redis veya ayrı bir veritabanında toplamanız gereken minimum metrik seti:

  • Şu anda havuzda aktif olan proxy sayısı.
  • Yasaklı proxy sayısı ve bunların geri dönüşü için ortalama süre.
  • Son N istekte her proxy için ortalama yanıt süresi.
  • Sunucular ve havuz genelinde başarılı isteklerin yüzdesi (başarı oranı).
  • Yük dağılımı - her sunucunun belirli bir süre içinde işlediği istek sayısı.

Bu metrikleri görselleştirmek için, bu verileri Redis'ten okuyup Grafana'ya anlaşılır bir formatta veren basit bir ihracatçı aracılığıyla Prometheus'a koymak uygundur. Bu, belirli bir coğrafya veya proxy türü için başarı oranındaki düşüşü hızlı bir şekilde görmenizi sağlar ve ayrıştırıcı tamamen durmadan önce havuza taze IP eklemenize olanak tanır.

Yeni sunucuların kümede başlatılması sırasında yasakların zirvelerini ayrıca izlemek önemlidir - eğer 3 sunucudan 6 sunucuya ölçeklenirken yasak oranı aniden artarsa, muhtemelen sorun shardlamadadır: yeni sunucular eski proxy aralıklarıyla kesişmeye başladı. Bu, modül ile bölmeden tutarlı hashlemeye geçmeniz gerektiğinin ilk işareti.

Dağıtık ayrıştırıcının başlatılması için kontrol listesi

Ayrıştırıcıyı birden fazla sunucuya dağıtmadan önce, aşağıdaki maddeleri kontrol edin:

  • Proxy havuzunun koordinasyon mekanizması seçildi: statik bölme, tutarlı hashleme veya Redis üzerinde merkezi broker.
  • Proxy'lerin atomik kilidi uygulanarak, bir IP'nin iki sunucu tarafından aynı anda kullanılmasını engellemiştir.
  • Proxy'nin "kilitli" durumda sonsuza dek kalmaması için yasaklama ve kilit için TTL ayarlandı.
  • Kullanıcı oturumu taklidi gerektiren görevler için, tüm görev süresi boyunca yapışkan proxy bağlanması ayarlandı.
  • Yasak tespiti yalnızca HTTP koduna değil, yanıt içeriğine göre de ayarlandı.
  • Tüm küme boyunca başarı oranı, aktif ve yasaklı proxy'lerin izlenmesi sağlandı.
  • Proxy havuzu görev türlerine göre bölündü: veri merkezi IP'leri - daha az hassas istekler için, rezidans veya mobil proxy'ler - gerçek kullanıcıya en yakın davranışın önemli olduğu oturumlar için.
  • Durum deposunun (Redis) yükü kontrol edildi - çok sayıda sunucu ve yüksek istek sıklığı durumunda, bir Redis örneği yerine Redis kümesi gerekebilir.

Sonuç

Ayrıştırıcının birden fazla sunucuya geçişindeki çakışmaların ana nedeni, proxy havuzunun durumu hakkında tek bir doğru kaynağın olmamasıdır. Merkezi bir broker ile atomik kilitler (Redis veya benzeri bir depolama üzerinde) ortaya çıktığında, IP tekrarları ve yasakların senkronizasyonu sorunları kendiliğinden ortadan kalkar. Sonrasında, kümenizin ölçeğine uygun bir shardlama stratejisi seçmek kalır: sabit sayıda sunucular için basit bölme yeterlidir, dinamik olarak büyüyen bir altyapı için ise tutarlı hashleme ile başlamanız daha iyidir.

Eğer dağıtık ayrıştırıcınız pazar yerleri veya sosyal medya ile çalışıyorsa ve trafik düzeyinde oturum başına inandırıcılık önemliyse, havuzu görev türlerine göre bölmek mantıklıdır: büyük katalog taramaları için ucuz veri merkezi proxy'leri uygunken, kullanıcı davranışını taklit eden oturumlar için rezidans proxy'leri gerçek ev IP'leri ile, bu da dolandırıcılığa karşı hassas sistemlerde yasak oranını önemli ölçüde azaltır.