Wenn Ihr Umsatz von der automatisierten Datenerfassung abhängt, bedeutet ein Ausfall der CAPTCHA-Lösung Datenverlust und gebrochene SLAs. Das Hochverfügbarkeitsdesign (HA) stellt sicher, dass Ihre Pipeline auch bei Teilausfällen – Worker-Abstürzen, Netzwerkproblemen oder API-Schluckauf – weiterläuft.
HA-Komponenten
[Health Checker] ──── monitors ────→ [Worker Pool A]
↓ ↓ (primary)
[Circuit Breaker] [CaptchaAI API]
↓ ↑ (fallback)
[Failover Router] ── redirects ──→ [Worker Pool B]
↓
[Dead Letter Queue] ← unrecoverable failures
Schicht 1: Arbeiterredundanz
Führen Sie mehr Worker aus, als Sie benötigen. Wenn einer ausfällt, übernehmen die verbleibenden Arbeiter die Last.
Python – Überwachter Worker-Pool
import os
import time
import threading
import queue
import requests
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
task_queue = queue.Queue(maxsize=200)
results = {}
class SupervisedWorkerPool:
def __init__(self, worker_count, min_workers=2):
self.worker_count = worker_count
self.min_workers = min_workers
self.workers = {}
self.lock = threading.Lock()
def start(self):
"""Launch workers and supervisor."""
for i in range(self.worker_count):
self._launch_worker(i)
# Supervisor thread monitors worker health
supervisor = threading.Thread(target=self._supervise, daemon=True)
supervisor.start()
def _launch_worker(self, worker_id):
t = threading.Thread(
target=self._worker_loop,
args=(worker_id,),
daemon=True
)
t.start()
with self.lock:
self.workers[worker_id] = {
"thread": t,
"alive": True,
"last_heartbeat": time.time(),
"tasks_completed": 0
}
def _worker_loop(self, worker_id):
session = requests.Session()
while True:
try:
task = task_queue.get(timeout=30)
result = solve_captcha(session, task)
results[task["task_id"]] = result
with self.lock:
self.workers[worker_id]["last_heartbeat"] = time.time()
self.workers[worker_id]["tasks_completed"] += 1
task_queue.task_done()
except queue.Empty:
# Heartbeat even when idle
with self.lock:
self.workers[worker_id]["last_heartbeat"] = time.time()
except Exception as e:
print(f"Worker {worker_id} error: {e}")
with self.lock:
self.workers[worker_id]["last_heartbeat"] = time.time()
def _supervise(self):
"""Restart dead workers."""
while True:
time.sleep(15)
with self.lock:
now = time.time()
for wid, info in list(self.workers.items()):
if not info["thread"].is_alive():
print(f"Worker {wid} died — restarting")
self._launch_worker(wid)
elif now - info["last_heartbeat"] > 120:
print(f"Worker {wid} stalled — replacing")
self._launch_worker(wid)
@property
def status(self):
with self.lock:
alive = sum(1 for w in self.workers.values()
if w["thread"].is_alive())
return {
"alive": alive,
"total": len(self.workers),
"healthy": alive >= self.min_workers
}
def solve_captcha(session, task):
resp = session.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": task.get("method", "userrecaptcha"),
"googlekey": task["sitekey"],
"pageurl": task["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 = session.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 {"solution": result["request"]}
if result.get("request") != "CAPCHA_NOT_READY":
return {"error": result.get("request")}
return {"error": "TIMEOUT"}
# Start pool with 8 workers, minimum 3 healthy
pool = SupervisedWorkerPool(worker_count=8, min_workers=3)
pool.start()
Schicht 2: Leistungsschalter
Erkennen Sie, wenn CaptchaAI Probleme hat, und stoppen Sie das Senden von Anfragen, um eine Verschwendung von Guthaben durch Zeitüberschreitungen zu vermeiden:
JavaScript
class CircuitBreaker {
constructor(options = {}) {
this.failureThreshold = options.failureThreshold || 5;
this.resetTimeout = options.resetTimeout || 60000; // 1 minute
this.failures = 0;
this.lastFailure = 0;
this.state = "closed"; // closed, open, half-open
this.successesInHalfOpen = 0;
}
async execute(fn) {
if (this.state === "open") {
if (Date.now() - this.lastFailure > this.resetTimeout) {
this.state = "half-open";
this.successesInHalfOpen = 0;
} else {
throw new Error("Circuit breaker is OPEN — requests blocked");
}
}
try {
const result = await fn();
this._onSuccess();
return result;
} catch (err) {
this._onFailure();
throw err;
}
}
_onSuccess() {
if (this.state === "half-open") {
this.successesInHalfOpen++;
if (this.successesInHalfOpen >= 3) {
this.state = "closed";
this.failures = 0;
console.log("Circuit breaker CLOSED — service recovered");
}
} else {
this.failures = 0;
}
}
_onFailure() {
this.failures++;
this.lastFailure = Date.now();
if (this.failures >= this.failureThreshold) {
this.state = "open";
console.log(
`Circuit breaker OPEN — ${this.failures} consecutive failures`
);
}
}
}
// Usage
const breaker = new CircuitBreaker({
failureThreshold: 5,
resetTimeout: 60000,
});
async function solveCaptchaWithBreaker(sitekey, pageurl) {
return breaker.execute(() => solveCaptcha(sitekey, pageurl));
}
Schicht 3: Endpunkt für die Gesundheitsprüfung
Stellen Sie den Gesundheitsstatus für Load Balancer und Überwachung bereit:
Python (Flasche)
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/health")
def health_check():
pool_status = pool.status
queue_depth = task_queue.qsize()
health = {
"status": "healthy" if pool_status["healthy"] else "degraded",
"workers_alive": pool_status["alive"],
"workers_total": pool_status["total"],
"queue_depth": queue_depth,
"queue_capacity": task_queue.maxsize
}
code = 200 if health["status"] == "healthy" else 503
return jsonify(health), code
@app.route("/health/ready")
def readiness_check():
"""Readiness probe — is this instance ready to receive tasks?"""
if pool.status["alive"] > 0 and task_queue.qsize() < task_queue.maxsize:
return "ready", 200
return "not ready", 503
Schicht 4: Anmutige Degradierung
Wenn etwas schief geht, verschlechtern Sie sich sanft, anstatt völlig zu scheitern:
class GracefulDegradation:
def __init__(self):
self.mode = "normal" # normal, degraded, emergency
def set_mode(self, error_rate, queue_depth, workers_alive):
if workers_alive == 0 or error_rate > 0.5:
self.mode = "emergency"
elif error_rate > 0.2 or queue_depth > 150:
self.mode = "degraded"
else:
self.mode = "normal"
def should_accept_task(self, priority):
if self.mode == "normal":
return True
if self.mode == "degraded":
return priority in ("high", "critical")
return priority == "critical" # Emergency: critical only
@property
def status(self):
return {
"mode": self.mode,
"accepting": {
"normal": self.mode == "normal",
"degraded": self.mode in ("normal", "degraded"),
"emergency": True
}
}
HA-Checkliste
| Komponente | Umgesetzt? | Notizen |
|---|---|---|
| Mehrere Arbeiter (N+1) | Ja | Mindestens 1 Ersatzarbeiter |
| Gesundheitsüberwachung der Arbeitnehmer | Ja | Supervisor-Thread oder Prozessmanager |
| Automatischer Worker-Neustart | Ja | Bei Absturz oder Stillstand |
| Leistungsschalter | Ja | Stoppen Sie Anfragen bei API-Problemen |
| Endpunkt für die Integritätsprüfung | Ja | Für Load Balancer |
| Anmutige Erniedrigung | Ja | Prioritätsbasierte Aufgabenannahme |
| Warteschlange für unzustellbare Nachrichten | Ja | Für nicht behebbare Ausfälle |
| Fallback-Umfrage | Ja | Wenn Rückrufe fehlschlagen |
| Alarmierung | Ja | PagerDuty, Slack, E-Mail |
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Worker ist erreichbar, verarbeitet aber keine Aufgaben | Queue, Credentials oder Eingabestrom stimmen nicht | Prüfe Queue-Tiefe, API-Key, Health-Checks und Fehlerraten pro Worker gemeinsam |
| Fehlerrate steigt nach Rollout | Neue Version verändert Session-, Proxy- oder Retry-Verhalten | Vergleiche erfolgreiche und fehlschlagende Runs zwischen alter und neuer Version und rolle bei Bedarf zurück |
| Canary oder Health-Check bleibt rot | Abhängigkeiten, Zeitlimits oder Secrets weichen von der Zielumgebung ab | Prüfe Secrets, Netzwerkpfade und Schwellenwerte in exakt derselben Umgebung |
Verwandte Leitfäden
- Disaster Recovery für CAPTCHA-Lösung
- Multi-Region-Architektur für CAPTCHA-Lösung
- Circuit-Breaker-Muster für CAPTCHA-API-Aufrufe
Diskussionen (0)
Beteiligen Sie sich an der Unterhaltung
Melden Sie sich an, um Ihre Meinung zu teilen.
AnmeldenNoch keine Kommentare.