CAPTCHA çözme API'si yavaş olduğunda veya hata döndürdüğünde, istek göndermeye devam etmek zaman ve para kaybına neden olur. Devre kesici modeli, arızalı bir API'nin çağrılmasını durdurur, kurtarmayı bekler ve otomatik olarak devam ederek işlem hattınızda art arda arızaların oluşmasını önler.
Devre kesiciler nasıl çalışır?
Üç durum:
- Kapalı – Normal çalışma. Talepler geçiyor. Başarısızlıklar sayılır.
- Açık – Çok fazla hata var. Tüm istekler, API çağrılmadan hemen reddedilir.
- Yarı Açık – Bir soğuma süresinden sonra bir test isteğinin geçmesine izin verilir. Başarılı olursa devre kapanır. Başarısız olursa devre tekrar açılır.
Python uygulaması
import time
import threading
import requests
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
API_KEY = "YOUR_API_KEY"
class CircuitBreaker:
def __init__(self, failure_threshold=5, recovery_timeout=60):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.failure_count = 0
self.last_failure_time = 0
self.state = "closed" # closed, open, half-open
self._lock = threading.Lock()
def call(self, func, *args, **kwargs):
with self._lock:
if self.state == "open":
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = "half-open"
print("[circuit] State: half-open — testing one request")
else:
remaining = self.recovery_timeout - (
time.time() - self.last_failure_time
)
raise CircuitOpenError(
f"Circuit open — retry in {remaining:.0f}s"
)
try:
result = func(*args, **kwargs)
with self._lock:
self.failure_count = 0
if self.state == "half-open":
print("[circuit] State: closed — API recovered")
self.state = "closed"
return result
except Exception as e:
with self._lock:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "open"
print(
f"[circuit] State: open — "
f"{self.failure_count} failures"
)
raise
class CircuitOpenError(Exception):
pass
def solve_captcha(sitekey, page_url):
resp = requests.post(SUBMIT_URL, data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": "1",
}, timeout=15)
data = resp.json()
if data["status"] != 1:
raise Exception(f"Submit error: {data['request']}")
task_id = data["request"]
for _ in range(24):
time.sleep(5)
poll = requests.get(RESULT_URL, params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": "1",
}, timeout=15).json()
if poll["status"] == 1:
return poll["request"]
if poll["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Poll error: {poll['request']}")
raise TimeoutError(f"Task {task_id} timed out")
# Usage
breaker = CircuitBreaker(failure_threshold=3, recovery_timeout=30)
for i in range(10):
try:
token = breaker.call(
solve_captcha, "6Le-SITEKEY", "https://example.com"
)
print(f"[task-{i}] Solved: {token[:40]}...")
except CircuitOpenError as e:
print(f"[task-{i}] Skipped: {e}")
except Exception as e:
print(f"[task-{i}] Failed: {e}")
Beklenen çıktı:
[task-0] Solved: 03AGdBq26ZfPxL...
[task-1] Solved: 03AGdBq27AbCdE...
[task-2] Failed: Submit error: ERROR_NO_SLOT_AVAILABLE
[task-3] Failed: Submit error: ERROR_NO_SLOT_AVAILABLE
[task-4] Failed: Submit error: ERROR_NO_SLOT_AVAILABLE
[circuit] State: open — 3 failures
[task-5] Skipped: Circuit open — retry in 28s
[task-6] Skipped: Circuit open — retry in 25s
...
[circuit] State: half-open — testing one request
[task-8] Solved: 03AGdBq28FgHiJ...
[circuit] State: closed — API recovered
JavaScript uygulaması
class CircuitBreaker {
constructor(options = {}) {
this.failureThreshold = options.failureThreshold || 5;
this.recoveryTimeout = options.recoveryTimeout || 60000;
this.failureCount = 0;
this.lastFailureTime = 0;
this.state = 'closed';
}
async call(fn, ...args) {
if (this.state === 'open') {
if (Date.now() - this.lastFailureTime > this.recoveryTimeout) {
this.state = 'half-open';
console.log('[circuit] State: half-open');
} else {
const remaining = this.recoveryTimeout - (Date.now() - this.lastFailureTime);
throw new Error(`Circuit open — retry in ${Math.ceil(remaining / 1000)}s`);
}
}
try {
const result = await fn(...args);
this.failureCount = 0;
if (this.state === 'half-open') {
console.log('[circuit] State: closed — recovered');
}
this.state = 'closed';
return result;
} catch (error) {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) {
this.state = 'open';
console.log(`[circuit] State: open — ${this.failureCount} failures`);
}
throw error;
}
}
}
// Usage
const axios = require('axios');
const API_KEY = 'YOUR_API_KEY';
const breaker = new CircuitBreaker({ failureThreshold: 3, recoveryTimeout: 30000 });
async function solveCaptcha(sitekey, pageurl) {
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) throw new Error(submit.data.request);
const taskId = submit.data.request;
for (let i = 0; i < 24; 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: taskId, json: 1 }
});
if (poll.data.status === 1) return poll.data.request;
if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
}
throw new Error('Timeout');
}
(async () => {
for (let i = 0; i < 10; i++) {
try {
const token = await breaker.call(solveCaptcha, '6Le-SITEKEY', 'https://example.com');
console.log(`[task-${i}] Solved: ${token.substring(0, 40)}...`);
} catch (err) {
console.log(`[task-${i}] ${err.message}`);
}
}
})();
Eşikleri seçme
| Parametre | Düşük trafik (< 10/min) | Yüksek trafik (> 100/min) |
|---|---|---|
failure_threshold |
3 | 10 |
recovery_timeout |
30'lar | 60'lar |
Arıza eşiğini, aralıklı hataları tolere edecek kadar yüksek (tek bir zaman aşımı devreyi tetiklememelidir), ancak arızalı bir API'yi etkilemeyi durduracak kadar da düşük ayarlayın.
Yeniden deneme mantığıyla birleştirme
Devre kesicinin içindeki yeniden deneme mantığını kullanın. Devre kesici son arızaları sayar (yeniden denemeler bittikten sonra):
def solve_with_retry(sitekey, page_url, max_retries=2):
for attempt in range(max_retries + 1):
try:
return solve_captcha(sitekey, page_url)
except Exception:
if attempt == max_retries:
raise
time.sleep(2 ** attempt)
# Circuit breaker wraps the retry function
token = breaker.call(solve_with_retry, "6Le-SITEKEY", "https://example.com")
Sorun giderme
| Sorun | Sebep | Düzeltme |
|---|---|---|
| Devre çok hızlı açılıyor | Eşik çok düşük | failure_threshold'yi artırın |
| Devre asla düzelmez | recovery_timeout çok uzun |
30-60 saniyeye düşürün |
| Çok iş parçacıklı kullanımda yarış durumu | Durumda kilit yok | threading.Lock (Python) veya atomik işlemleri kullanın |
| Kısmi kesinti sırasında tüm istekler engellendi | Tüm uç noktalar için tek kesici | Gönderme ve yoklama uç noktaları için ayrı kesiciler kullanın |
SSS
Gönderme ve yoklama için ayrı devre kesiciler mi kullanmalıyım?
Evet, büyük ölçekli sistemler için. Yoklama hala çalışıyorken gönderim uç noktası başarısız olabilir (veya tam tersi). Ayrı kesiciler size daha hassas kontrol sağlar.
Devre açıkken ne yapmalıyım?
CAPTCHA görevini daha sonra kullanmak üzere sıraya koyun, bir yedek kullanıcı arayüzü gösterin veya işlemi atlayın. GörmekÇözüm Başarısız Olduğunda Zarif Bozulma.
CaptchaAI ile esnek CAPTCHA iş akışları oluşturun
API anahtarınızı şu adresten alın:captchaai.com.