Wenn die CAPTCHA-Lösungs-API langsam ist oder Fehler zurückgibt, verschwendet das weitere Senden von Anfragen Zeit und Geld. Das Circuit-Breaker-Muster stoppt den Aufruf einer fehlerhaften API, wartet auf die Wiederherstellung und wird automatisch fortgesetzt – wodurch kaskadierende Fehler in Ihrer Pipeline verhindert werden.
Wie Leistungsschalter funktionieren
Drei Staaten:
- Geschlossen – Normaler Betrieb. Anfragen gehen durch. Ausfälle werden gezählt.
- Offen – Zu viele Fehler. Alle Anfragen werden sofort abgelehnt, ohne die API aufzurufen.
- Halboffen – Nach einer Abklingzeit wird eine Testanfrage durchgelassen. Gelingt dies, schließt sich der Stromkreis. Fällt dies aus, öffnet sich der Stromkreis erneut.
Python-Implementierung
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}")
Erwartete Ausgabe:
[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-Implementierung
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}`);
}
}
})();
Schwellenwerte wählen
| Parameter | Geringer Verkehr (< 10/min) | Hoher Traffic (> 100/min) |
|---|---|---|
failure_threshold |
3 | 10 |
recovery_timeout |
30er Jahre | 60er Jahre |
Legen Sie den Fehlerschwellenwert hoch genug fest, um intermittierende Fehler zu tolerieren (ein einziger Timeout sollte den Schaltkreis nicht auslösen), aber niedrig genug, um eine fehlerhafte API nicht mehr zu belasten.
Kombination mit Wiederholungslogik
Verwenden Sie die Wiederholungslogik innerhalb des Leistungsschalters. Der Leistungsschalter zählt die endgültigen Ausfälle (nachdem die Wiederholungsversuche ausgeschöpft sind):
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")
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Der Stromkreis öffnet sich zu schnell | Schwelle zu niedrig | Erhöhen Sie failure_threshold |
| Der Stromkreis erholt sich nie wieder | recovery_timeout zu lang |
Auf 30-60 Sekunden reduzieren |
| Racebedingung im Multithread-Einsatz | Kein Lock-on-Status | Verwenden Sie threading.Lock (Python) oder atomare Operationen |
| Alle Anfragen wurden während eines teilweisen Ausfalls blockiert | Ein einziger Leistungsschalter für alle Endpunkte | Verwenden Sie separate Leistungsschalter für Submit- und Poll-Endpunkte |
FAQ
Sollte ich für Übermittlung und Abfrage separate Leistungsschalter verwenden?
Ja, für Großanlagen. Der Übermittlungsendpunkt schlägt möglicherweise fehl, während die Abfrage noch funktioniert (oder umgekehrt). Separate Leistungsschalter sorgen für eine feinere Kontrolle.
Was soll ich tun, wenn der Stromkreis offen ist?
Stellen Sie die CAPTCHA-Aufgabe für später in die Warteschlange, zeigen Sie eine Fallback-Benutzeroberfläche an oder überspringen Sie den Vorgang. SehenAnmutige Verschlechterung, wenn die Lösung fehlschlägt.
Erstellen Sie belastbare CAPTCHA-Workflows mit CaptchaAI
Holen Sie sich Ihren API-Schlüssel unter captchaai.com.
Verwandte Leitfäden
- Retry und Fehlerbehandlung in Node.js
- API-Antwortformate und Fehlercodes
- Graceful Degradation bei fehlgeschlagener CAPTCHA-Lösung
Diskussionen (0)
Beteiligen Sie sich an der Unterhaltung
Melden Sie sich an, um Ihre Meinung zu teilen.
AnmeldenNoch keine Kommentare.