Tutorials

Dağıtılmış Çalışanlar Genelinde CAPTCHA Oturumu Durum Yönetimi

Birden fazla çalışan aynı site için CAPTCHA'ları çözdüğünde ortak bir sorunu paylaşırlar: her çalışanın kendi oturumu vardır. Hedef site farklı çerezleri, farklı IP'leri ve farklı tarayıcı parmak izlerini görür. Oturum durumu yönetimi, bağlamı çalışanlar arasında senkronize ederek çözümlerin tutarlı olmasını ve hedef sitenin tutarlı oturumlar görmesini sağlar.

Oturum Durumu Sorunu

Worker 1 → Login → Solve CAPTCHA → Get cookie A → Submit form ✅
Worker 2 → New session → Solve CAPTCHA → Get cookie B → Submit form ✅
Worker 3 → Reuse cookie A? → Cookie expired → Solve CAPTCHA → Fail ❌

Paylaşılan durum olmadan çalışanlar, süresi dolmuş oturumlardaki çözümleri boşa harcar ve hedef sitelerin algılayabileceği tutarsız davranışlar üretir.

Oturum Durumu Neleri İçerir?

Durum Bileşeni Ömür boyu Paylaşım Stratejisi
Kimlik doğrulama çerezleri Dakikalardan saatlere TTL ile Redis
CAPTCHA belirteçleri 90–300 saniye Redis listesi (kısa TTL)
qa_session_cookie çerezleri ~30 dakika Redis karma
CSRF belirteçleri Sayfa yükü başına Paylaşmayın; her çalışanın kendine ait bir hakkı var
Tarayıcı test profil yapılandırması Kalıcı Çalışma zamanı durumu değil yapılandırma
Vekil ataması Oturum başına Redis destekli proxy havuzu

Mimarlık

┌──────────────────────────────────────┐
│          Session State Store          │
│              (Redis)                  │
│                                      │
│  cookies:{domain} → Hash             │
│  tokens:{sitekey} → List             │
│  proxies:pool → Set                  │
│  locks:{domain}:{worker} → String    │
└─────┬──────────┬──────────┬──────────┘
      │          │          │
  ┌───▼───┐  ┌──▼────┐  ┌──▼────┐
  │Worker1│  │Worker2│  │Worker3│
  └───────┘  └───────┘  └───────┘

Python Uygulaması

Oturum Deposu

import os
import json
import time
import redis
import requests
from datetime import datetime, timezone

r = redis.Redis(
    host=os.environ.get("REDIS_HOST", "localhost"),
    port=int(os.environ.get("REDIS_PORT", 6379)),
    decode_responses=True
)

API_KEY = os.environ["CAPTCHAAI_API_KEY"]


class SessionStore:
    """Shared session state across distributed workers."""

    def __init__(self, domain):
        self.domain = domain
        self.cookie_key = f"session:cookies:{domain}"
        self.token_key = f"session:tokens:{domain}"

    def save_cookies(self, cookies, ttl=1800):
        """Store cookies from a successful session."""
        cookie_data = {name: value for name, value in cookies.items()}
        r.hset(self.cookie_key, mapping=cookie_data)
        r.expire(self.cookie_key, ttl)

    def get_cookies(self):
        """Retrieve shared cookies."""
        cookies = r.hgetall(self.cookie_key)
        return cookies if cookies else None

    def save_token(self, sitekey, token, ttl=80):
        """Store a solved CAPTCHA token."""
        key = f"{self.token_key}:{sitekey}"
        r.rpush(key, token)
        r.expire(key, ttl)

    def get_token(self, sitekey):
        """Pop a cached CAPTCHA token."""
        key = f"{self.token_key}:{sitekey}"
        return r.lpop(key)

    def acquire_session_lock(self, worker_id, ttl=300):
        """Ensure only one worker manages the session at a time."""
        lock_key = f"session:lock:{self.domain}"
        return r.set(lock_key, worker_id, nx=True, ex=ttl)

    def release_session_lock(self, worker_id):
        """Release session lock if this worker holds it."""
        lock_key = f"session:lock:{self.domain}"
        current = r.get(lock_key)
        if current == worker_id:
            r.delete(lock_key)

Paylaşılan Duruma Sahip Çalışan

class CaptchaWorker:
    def __init__(self, worker_id, domain):
        self.worker_id = worker_id
        self.store = SessionStore(domain)
        self.session = requests.Session()

    def setup_session(self):
        """Load shared cookies into this worker's session."""
        cookies = self.store.get_cookies()
        if cookies:
            for name, value in cookies.items():
                self.session.cookies.set(name, value)
            return True
        return False

    def solve_captcha(self, sitekey, pageurl):
        """Solve with token cache and session sharing."""
        # Check for cached token
        cached = self.store.get_token(sitekey)
        if cached:
            return {"solution": cached, "source": "cache"}

        # Solve via CaptchaAI
        resp = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": API_KEY,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": 1
        })
        data = resp.json()
        if data.get("status") != 1:
            return {"error": data.get("request")}

        captcha_id = data["request"]

        for _ in range(60):
            time.sleep(5)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": API_KEY, "action": "get",
                "id": captcha_id, "json": 1
            }).json()

            if result.get("status") == 1:
                token = result["request"]
                self.store.save_token(sitekey, token)
                return {"solution": token, "source": "api"}

            if result.get("request") != "CAPCHA_NOT_READY":
                return {"error": result.get("request")}

        return {"error": "TIMEOUT"}

    def process_page(self, url, sitekey):
        """Full workflow: setup session → solve CAPTCHA → submit."""
        # Load shared session
        self.setup_session()

        # Solve CAPTCHA
        result = self.solve_captcha(sitekey, url)
        if "error" in result:
            return result

        # Submit form with token
        response = self.session.post(url, data={
            "g-recaptcha-response": result["solution"]
        })

        # Share resulting cookies
        self.store.save_cookies(dict(self.session.cookies))

        return {"status": response.status_code, "source": result["source"]}

Proxy Havuzu Yönetimi

class ProxyPool:
    """Distribute proxies across workers to avoid IP conflicts."""

    def __init__(self, proxies):
        self.pool_key = "session:proxy_pool"
        self.assigned_key = "session:proxy_assigned"
        # Initialize pool
        for proxy in proxies:
            r.sadd(self.pool_key, proxy)

    def acquire_proxy(self, worker_id, ttl=600):
        """Assign an unused proxy to a worker."""
        # Check if worker already has one
        existing = r.hget(self.assigned_key, worker_id)
        if existing:
            return existing

        # Pop from available pool
        proxy = r.spop(self.pool_key)
        if proxy:
            r.hset(self.assigned_key, worker_id, proxy)
            r.expire(self.assigned_key, ttl)
            return proxy
        return None

    def release_proxy(self, worker_id):
        """Return proxy to the pool."""
        proxy = r.hget(self.assigned_key, worker_id)
        if proxy:
            r.sadd(self.pool_key, proxy)
            r.hdel(self.assigned_key, worker_id)

JavaScript Uygulaması

const Redis = require("ioredis");
const axios = require("axios");

const redis = new Redis(process.env.REDIS_URL || "redis://localhost:6379");
const API_KEY = process.env.CAPTCHAAI_API_KEY;

class SessionStore {
  constructor(domain) {
    this.domain = domain;
    this.cookieKey = `session:cookies:${domain}`;
    this.tokenKey = `session:tokens:${domain}`;
  }

  async saveCookies(cookies, ttl = 1800) {
    const entries = Object.entries(cookies).flat();
    if (entries.length > 0) {
      await redis.hset(this.cookieKey, ...entries);
      await redis.expire(this.cookieKey, ttl);
    }
  }

  async getCookies() {
    return await redis.hgetall(this.cookieKey);
  }

  async saveToken(sitekey, token, ttl = 80) {
    const key = `${this.tokenKey}:${sitekey}`;
    await redis.rpush(key, token);
    await redis.expire(key, ttl);
  }

  async getToken(sitekey) {
    return await redis.lpop(`${this.tokenKey}:${sitekey}`);
  }

  async acquireLock(workerId, ttl = 300) {
    const result = await redis.set(`session:lock:${this.domain}`, workerId, "NX", "EX", ttl);
    return result === "OK";
  }

  async releaseLock(workerId) {
    const current = await redis.get(`session:lock:${this.domain}`);
    if (current === workerId) await redis.del(`session:lock:${this.domain}`);
  }
}

async function workerSolve(store, sitekey, pageurl) {
  const cached = await store.getToken(sitekey);
  if (cached) return { solution: cached, source: "cache" };

  const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: { key: API_KEY, method: "userrecaptcha", googlekey: sitekey, pageurl, json: 1 },
  });
  if (submit.data.status !== 1) return { error: submit.data.request };

  const captchaId = submit.data.request;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const poll = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });
    if (poll.data.status === 1) {
      await store.saveToken(sitekey, poll.data.request);
      return { solution: poll.data.request, source: "api" };
    }
    if (poll.data.request !== "CAPCHA_NOT_READY") return { error: poll.data.request };
  }
  return { error: "TIMEOUT" };
}

Devlet Yönetim Modelleri

Desen Ne Zaman Kullanılmalı
Oturum kilidi Bir çalışan oturum açma işlemini yönetir, diğerleri ise çerezleri kullanır
Jeton havuzu Yüksek verimlilik: belirteçleri önceden çözün ve dağıtın
Çerez paylaşımı Çalışanların kimliği doğrulanmış oturumlara ihtiyacı var
Vekil yakınlığı Hedef site IP oturumu bağlamayı izler

Sorun giderme

Sorun Sebep Düzeltme
Çalışanlar farklı oturumlar alıyor Redis aracılığıyla paylaşılmayan çerezler Başarılı isteklerden sonra save_cookies'nin çağrıldığını doğrulayın
Tokenın süresi, diğer çalışanlar onu kullanmadan önce doldu TTL çok uzun veya ağ gecikmesi TTL güvenlik marjını azaltın; Jetonları alındıktan sonraki 10 saniye içinde kullanın
Oturum kilidi hiçbir zaman yayınlanmadı İşçi kaza yaptı Kilit anahtarının otomatik serbest bırakılmasında TTL (varsayılan 300 saniye)
Hedef site çalışanları engelliyor Tüm çalışanlar aynı proxy'yi kullanıyor Çalışan başına benzeşimle proxy havuzunu kullanın

SSS

Her çalışan çerezleri paylaşmalı mı?

Yalnızca kimliği doğrulanmış oturumlar gerektiren siteler için. Durum bilgisi olmayan CAPTCHA çözümü için (site anahtarını gönderin – jeton alın), çalışanların paylaşılan çerezlere ihtiyacı yoktur; yalnızca paylaşılan jetonlar yeterlidir.

Oturumun sona ermesini nasıl halledebilirim?

Redis TTL'yi oturum ömründen biraz daha kısa bir süreye ayarlayın. Çerezlerin süresi dolduğunda, bir çalışan oturum kilidini alır, yeniden kimlik doğrulaması yapar ve diğerleri için yeni çerezler saklar.

Peki ya tarayıcı tabanlı oturumlar (Puppeteer/Playwright)?

Tarayıcı çerezlerini page.cookies() ile serileştirin ve Redis'te saklayın. Diğer çalışanlar onlara page.setCookie() yüklüyor. Bu, ayrı tarayıcı örneklerinde ve makinelerde çalışır.

Sonraki Adımlar

Dağıtılmış CAPTCHA çalışanlarınızı verimli bir şekilde koordine edin;CaptchaAI API anahtarınızı alın.

İlgili kılavuzlar:

Bu makale için yorumlar devre dışı bırakılmıştır.

İlgili Yazılar

DevOps & Scaling CAPTCHA Çözme Altyapısı için Mavi-Yeşil Dağıtım
Üretimdeki Captcha AI iş akışlarına yönelik mimari kararları, işletim hususlarını ve otomasyon modellerini içeren CAPTCHA Çözme Altyapısı için Mavi-Yeşil Dağıtı...

Üretimdeki Captcha AI iş akışlarına yönelik mimari kararları, işletim hususlarını ve otomasyon modellerini içe...

Apr 27, 2026
DevOps & Scaling Azure İşlevleri + CaptchaAI: Bulut Entegrasyonu
Azure İşlevleri + Captcha AI: Bulut Entegrasyonu için Dev Ops kılavuzu, üretimdeki Captcha AI iş akışlarına yönelik mimari kararları, işletim hususlarını ve oto...

Azure İşlevleri + Captcha AI: Bulut Entegrasyonu için Dev Ops kılavuzu, üretimdeki Captcha AI iş akışlarına yö...

Apr 23, 2026
DevOps & Scaling CaptchaAI Çalışan Dağıtımı için Ansible Playbook'lar
Ansible Playbook'lar için Captcha AI Çalışan Dağıtımı için Dev Ops kılavuzu, üretimdeki Captcha AI iş akışlarına yönelik mimari kararları, işletim hususlarını v...

Ansible Playbook'lar için Captcha AI Çalışan Dağıtımı için Dev Ops kılavuzu, üretimdeki Captcha AI iş akışları...

Apr 19, 2026