Eğitimler

Hız Sınırlı Eşzamanlılık: CAPTCHA API Çağrıları için Jeton Grubu

Kontrolsüz eşzamanlılık, istekleri olabildiğince hızlı gönderir. Bu, ERROR_TOO_MUCH_REQUESTS'ye, API bakiyesinin boşa harcanmasına ve öngörülemeyen maliyetlere yol açar. Bir jeton kümesi, "saniyede en fazla 20 gönderim" şeklinde kesin bir oran belirlemenize olanak tanırken, kapasite mevcut olduğunda yine de kısa patlamalara izin verir.

Token Kovası Nasıl Çalışır?

[Bucket] capacity=20, refill=10/sec

Time 0:  ████████████████████  20 tokens available
         → 15 requests consume 15 tokens
Time 0:  █████                 5 tokens remain

Time 1s: ███████████████       15 tokens (5 + 10 refilled)
         → 15 requests consume 15 tokens
Time 1s: (empty)               0 tokens

Time 2s: ██████████            10 tokens (0 + 10 refilled)
         → Request waits if bucket is empty

Anahtar özellikler:

  • Kapasite – maksimum seri çekim boyutu
  • Yeniden dolum oranı – saniye başına sürekli istek sayısı
  • İstekler paket boşaldığında bekler (reddetme yok, yalnızca azaltma)

Python Uygulaması

İş Parçacığı Güvenli Jeton Kovası

import time
import threading


class TokenBucket:
    def __init__(self, capacity, refill_rate):
        """
        Args:
            capacity: Maximum tokens (burst size)
            refill_rate: Tokens added per second
        """
        self.capacity = capacity
        self.refill_rate = refill_rate
        self.tokens = capacity
        self.last_refill = time.monotonic()
        self.lock = threading.Lock()

    def acquire(self, timeout=None):
        """Block until a token is available."""
        deadline = time.monotonic() + timeout if timeout else float("inf")

        while True:
            with self.lock:
                self._refill()
                if self.tokens >= 1:
                    self.tokens -= 1
                    return True

            # Check timeout
            if time.monotonic() >= deadline:
                return False

            # Wait before retrying (avoid busy loop)
            time.sleep(min(1.0 / self.refill_rate, 0.1))

    def _refill(self):
        now = time.monotonic()
        elapsed = now - self.last_refill
        new_tokens = elapsed * self.refill_rate
        self.tokens = min(self.capacity, self.tokens + new_tokens)
        self.last_refill = now

Hız Sınırlı CAPTCHA Çözücü

import os
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed

API_KEY = os.environ["CAPTCHAAI_API_KEY"]

# Allow 10 submissions/sec with burst of 20
rate_limiter = TokenBucket(capacity=20, refill_rate=10)


def solve_captcha_rate_limited(sitekey, pageurl):
    """Solve with rate limiting on submission."""
    # Wait for token before submitting
    rate_limiter.acquire()

    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:
        raise RuntimeError(data.get("request"))

    captcha_id = data["request"]

    # Polling doesn't need rate limiting (separate concern)
    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:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(result.get("request"))

    raise TimeoutError("Solve timeout")


# Run 100 tasks through rate limiter
tasks = [
    {"sitekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
     "pageurl": f"https://example.com/p/{i}"}
    for i in range(100)
]

with ThreadPoolExecutor(max_workers=30) as executor:
    futures = {
        executor.submit(
            solve_captcha_rate_limited, t["sitekey"], t["pageurl"]
        ): t for t in tasks
    }

    for future in as_completed(futures):
        task = futures[future]
        try:
            solution = future.result()
            print(f"[OK] {task['pageurl']}")
        except Exception as e:
            print(f"[ERR] {task['pageurl']}: {e}")

JavaScript Uygulaması

Eşzamansız Token Paketi

class TokenBucket {
  constructor(capacity, refillRate) {
    this.capacity = capacity;
    this.refillRate = refillRate; // tokens per second
    this.tokens = capacity;
    this.lastRefill = Date.now();
    this.waitQueue = [];
  }

  _refill() {
    const now = Date.now();
    const elapsed = (now - this.lastRefill) / 1000;
    this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
    this.lastRefill = now;
  }

  async acquire() {
    this._refill();

    if (this.tokens >= 1) {
      this.tokens -= 1;
      return;
    }

    // Wait until a token is available
    const waitTime = ((1 - this.tokens) / this.refillRate) * 1000;
    await new Promise((resolve) => setTimeout(resolve, waitTime));

    this._refill();
    this.tokens -= 1;
  }
}

Hız Sınırlı Toplu Çözücü

const axios = require("axios");

const API_KEY = process.env.CAPTCHAAI_API_KEY;
const rateLimiter = new TokenBucket(20, 10); // 20 burst, 10/sec sustained

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function solveCaptchaLimited(sitekey, pageurl) {
  // Wait for rate limit token
  await rateLimiter.acquire();

  const submitResp = await axios.post(
    "https://ocr.captchaai.com/in.php",
    null,
    {
      params: {
        key: API_KEY,
        method: "userrecaptcha",
        googlekey: sitekey,
        pageurl: pageurl,
        json: 1,
      },
    }
  );

  if (submitResp.data.status !== 1) {
    throw new Error(submitResp.data.request);
  }

  const captchaId = submitResp.data.request;

  for (let i = 0; i < 60; i++) {
    await sleep(5000);
    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });

    if (result.data.status === 1) return result.data.request;
    if (result.data.request !== "CAPCHA_NOT_READY") {
      throw new Error(result.data.request);
    }
  }

  throw new Error("TIMEOUT");
}

// Solve 100 tasks — rate limiter ensures max 10 submissions/sec
async function batchSolve(tasks) {
  const results = await Promise.allSettled(
    tasks.map((t) => solveCaptchaLimited(t.sitekey, t.pageurl))
  );

  const solved = results.filter((r) => r.status === "fulfilled").length;
  const failed = results.filter((r) => r.status === "rejected").length;
  console.log(`Solved: ${solved}, Failed: ${failed}`);
}

Parametrelerin Seçilmesi

İş yükü Kapasite (patlama) Yeniden dolum oranı (sürekli)
Hafif kazıma 5 2/sec
Standart otomasyon 20 10/sec
Yüksek hacimli boru hattı 50 30/sec
Maksimum verim 100 50/sec

Temel kurallar:

  • Kapasiteyi 2 × yeniden doldurma hızına ayarlayın (2 saniyelik patlamalara izin verir)
  • İhtiyatlı başlayın, hata oranlarını izlerken artırın
  • Yalnızca oran sınırı olan gönderimler; oylama hafiftir ve kendi kendini sınırlar

Token Kovası ve Diğer Algoritmalar

Algoritma Davranış Şunun için en iyisi:
Jeton paketi Patlama payı ile yumuşak hız CAPTCHA API çağrıları
Sızdıran kova Sabit çıkış hızı, patlama yok Katı oran gereksinimleri
Sabit pencere Zaman penceresi başına sayım, kenar patlamaları Basit sayaçlar
Sürgülü pencere Devam eden süreyi sayın Doğru oran uygulaması

Token kovası en iyi varsayılan seçenektir; sürdürülebilir bir oran uygularken doğal patlamalara izin verir (kazıyıcı aynı anda 20 CAPTCHA bulur).

Sorun giderme

Sorun Sebep Düzeltme
İstekler hâlâ kısıtlanıyor Hız sınırlayıcı API'nin izin verdiğinden daha yükseğe ayarlandı CaptchaAI'nin limitlerine uyacak şekilde daha düşük yeniden dolum oranı
İsteklerde yüksek gecikme Jetonlar tükendi, yeniden doldurulmayı bekliyor Patlama senaryoları için kapasiteyi artırın
Bellek büyüyor Bekleme kuyruğu birikiyor Maksimum kuyruk boyutunu ayarlayın; fazla istekleri reddet
Hız sınırlayıcı işlemler arasında paylaşılmıyor Yalnızca bellek içi Dağıtılmış hız sınırlaması için Redis tabanlı jeton grubunu kullanın

SSS

Gönderimlere, oylamaya veya her ikisine de oran sınırı koymalı mıyım?

Yalnızca oran sınırı gönderimleri. Yoklama istekleri hafiftir ve time.sleep(5) aracılığıyla kendi kendine kısılır. Aşırı sınırlayıcı yoklama artışları gecikmeyi hiçbir fayda sağlamadan çözer.

Hız sınırlamasına rağmen ERROR_TOO_MUCH_REQUESTS'yi nasıl halledebilirim?

Hız sınırınız çok yüksek ayarlanmış. Doldurma oranını düşürün. Ayrıca birden fazla işlemin aynı API anahtarını paylaşıp paylaşmadığını, yani tüm işlemler genelindeki toplam ücreti kontrol edin.

CAPTCHA türüne göre hız sınırlayıcı kullanabilir miyim?

Evet; farklı CAPTCHA türleri için ayrı jeton paketleri oluşturun. Bu, yüksek hacimli reCAPTCHA v2 görevlerinin Turnstile gönderimlerini yetersiz hale getirmesini önler.

İlgili Makaleler

Sonraki Adımlar

Hız kontrollü CAPTCHA çözümü oluşturun —CaptchaAI API anahtarınızı alınve sürdürülebilir talep oranlarının uygulanması.

İlgili kılavuzlar:

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