RabbitMQ bietet garantierte Zustellung, Nachrichtenbestätigung und ausgefeiltes Routing für CAPTCHA-Lösungs-Workloads. Dieser Leitfaden erstellt eine produktionsbereite Integration.
Warum RabbitMQ für die CAPTCHA-Lösung
| Funktion | Profitieren |
|---|---|
| Dauerhafte Warteschlangen | Aufgaben überleben den Neustart des Brokers |
| Nachrichtenbestätigung | Keine verlorenen Aufgaben bei einem Worker-Absturz |
| Austausch toter Briefe | Fehlgeschlagene Aufgaben werden zur Untersuchung weitergeleitet |
| Prioritätswarteschlangen | Dringende CAPTCHAs werden zuerst gelöst |
| Routing-Schlüssel | Weiterleitung nach CAPTCHA-Typ an Fachkräfte |
Einrichtung
# Docker
docker run -d --hostname rabbitmq \
-p 5672:5672 -p 15672:15672 \
rabbitmq:3-management
# Python client
pip install pika requests
Produzent: Aufgaben einreichen
import json
import uuid
import pika
class CaptchaProducer:
"""Submit CAPTCHA tasks to RabbitMQ."""
def __init__(self, rabbitmq_url="amqp://guest:guest@localhost:5672/"):
self.connection = pika.BlockingConnection(
pika.URLParameters(rabbitmq_url),
)
self.channel = self.connection.channel()
self._setup_queues()
def _setup_queues(self):
"""Declare durable queues and exchanges."""
# Dead letter exchange for failed tasks
self.channel.exchange_declare(
exchange="captcha.dlx",
exchange_type="direct",
durable=True,
)
self.channel.queue_declare(
queue="captcha.failed",
durable=True,
)
self.channel.queue_bind(
queue="captcha.failed",
exchange="captcha.dlx",
routing_key="failed",
)
# Main task queue with dead letter routing
self.channel.queue_declare(
queue="captcha.tasks",
durable=True,
arguments={
"x-dead-letter-exchange": "captcha.dlx",
"x-dead-letter-routing-key": "failed",
"x-message-ttl": 300000, # 5 min TTL
},
)
# Results queue
self.channel.queue_declare(
queue="captcha.results",
durable=True,
)
def submit(self, method, params, priority=0):
"""Submit a CAPTCHA task."""
task_id = str(uuid.uuid4())[:8]
task = {
"id": task_id,
"method": method,
"params": params,
}
self.channel.basic_publish(
exchange="",
routing_key="captcha.tasks",
body=json.dumps(task),
properties=pika.BasicProperties(
delivery_mode=2, # Persistent
priority=priority,
message_id=task_id,
),
)
return task_id
def close(self):
self.connection.close()
# Usage
producer = CaptchaProducer()
task_id = producer.submit("userrecaptcha", {
"googlekey": "SITE_KEY",
"pageurl": "https://example.com",
}, priority=5)
print(f"Submitted: {task_id}")
producer.close()
Verbraucher: Arbeiter
import json
import os
import time
import pika
import requests
class CaptchaConsumer:
"""RabbitMQ consumer that solves CAPTCHAs."""
def __init__(self, api_key, rabbitmq_url="amqp://guest:guest@localhost:5672/"):
self.api_key = api_key
self.base = "https://ocr.captchaai.com"
self.connection = pika.BlockingConnection(
pika.URLParameters(rabbitmq_url),
)
self.channel = self.connection.channel()
# Process one task at a time
self.channel.basic_qos(prefetch_count=1)
def start(self):
"""Start consuming tasks."""
self.channel.basic_consume(
queue="captcha.tasks",
on_message_callback=self._handle_task,
)
print("Worker started. Waiting for tasks...")
self.channel.start_consuming()
def _handle_task(self, ch, method, properties, body):
"""Process a single CAPTCHA task."""
task = json.loads(body)
task_id = task["id"]
print(f"Processing {task_id}...")
try:
token = self._solve(task["method"], task["params"])
# Publish result
result = {
"task_id": task_id,
"status": "success",
"token": token,
}
ch.basic_publish(
exchange="",
routing_key="captcha.results",
body=json.dumps(result),
properties=pika.BasicProperties(delivery_mode=2),
)
# Acknowledge message (remove from queue)
ch.basic_ack(delivery_tag=method.delivery_tag)
print(f"{task_id} solved successfully")
except Exception as e:
print(f"{task_id} failed: {e}")
# Reject and send to dead letter queue
ch.basic_nack(
delivery_tag=method.delivery_tag,
requeue=False, # Goes to DLX
)
def _solve(self, captcha_method, params, timeout=120):
resp = requests.post(f"{self.base}/in.php", data={
"key": self.api_key,
"method": captcha_method,
"json": 1,
**params,
}, timeout=30)
result = resp.json()
if result.get("status") != 1:
raise RuntimeError(result.get("request"))
captcha_id = result["request"]
start = time.time()
while time.time() - start < timeout:
time.sleep(5)
resp = requests.get(f"{self.base}/res.php", params={
"key": self.api_key,
"action": "get",
"id": captcha_id,
"json": 1,
}, timeout=15)
data = resp.json()
if data["request"] != "CAPCHA_NOT_READY":
if data.get("status") == 1:
return data["request"]
raise RuntimeError(data["request"])
raise TimeoutError("Solve timeout")
# Run worker
if __name__ == "__main__":
consumer = CaptchaConsumer(os.environ["CAPTCHAAI_KEY"])
consumer.start()
Ergebniskollektor
import json
import pika
class ResultCollector:
"""Collect task results from the results queue."""
def __init__(self, rabbitmq_url="amqp://guest:guest@localhost:5672/"):
self.connection = pika.BlockingConnection(
pika.URLParameters(rabbitmq_url),
)
self.channel = self.connection.channel()
self.results = {}
def collect(self, expected_count, timeout=120):
"""Collect a specific number of results."""
deadline = time.time() + timeout
while len(self.results) < expected_count and time.time() < deadline:
method, _, body = self.channel.basic_get(
queue="captcha.results",
auto_ack=True,
)
if body:
result = json.loads(body)
self.results[result["task_id"]] = result
time.sleep(0.5)
return self.results
Typbasiertes Routing
Leiten Sie verschiedene CAPTCHA-Typen an spezialisierte Mitarbeiter weiter:
# Setup exchanges and queues
channel.exchange_declare(
exchange="captcha.types",
exchange_type="direct",
durable=True,
)
# Queue per type
for captcha_type in ["recaptcha", "turnstile", "image"]:
channel.queue_declare(queue=f"captcha.{captcha_type}", durable=True)
channel.queue_bind(
queue=f"captcha.{captcha_type}",
exchange="captcha.types",
routing_key=captcha_type,
)
# Submit with routing
def submit_routed(channel, captcha_type, task):
channel.basic_publish(
exchange="captcha.types",
routing_key=captcha_type,
body=json.dumps(task),
properties=pika.BasicProperties(delivery_mode=2),
)
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Nachrichten gingen bei Absturz verloren | Nicht dauerhafte Warteschlange | Legen Sie durable=True und delivery_mode=2 fest |
| Der Arbeiter blieb bei einer Aufgabe hängen | Langes CAPTCHA lösen | Legen Sie prefetch_count=1 pro Worker fest |
| Die Warteschlange für unzustellbare Nachrichten wächst | Anhaltende Fehler | Überprüfen Sie fehlgeschlagene Aufgaben und korrigieren Sie Parameter |
| Verbindung bricht ab | Heartbeat-Timeout | Legen Sie das Heartbeat-Intervall fest und fügen Sie eine Wiederverbindungslogik hinzu |
FAQ
Wann sollte ich RabbitMQ anstelle von Redis verwenden?
Verwenden Sie RabbitMQ, wenn Sie eine garantierte Zustellung, die Weiterleitung unzustellbarer Nachrichten oder eine typbasierte Nachrichtenweiterleitung benötigen. Verwenden Sie Redis für einfachere Setups mit geringerer Latenz.
Wie viele Verbraucher sollte ich betreiben?
Ein Verbraucher pro CPU-Kern funktioniert gut. Jeder Verbraucher verarbeitet jeweils eine Aufgabe (prefetch_count=1), also 4 Kerne = 4 Verbraucher.
Kann ich fehlgeschlagene Aufgaben automatisch wiederholen?
Ja. Konfigurieren Sie einen Wiederholungsaustausch mit einer TTL-Verzögerung. Von Mitarbeitern abgelehnte Nachrichten werden verzögert und automatisch wieder in die Warteschlange gestellt.
Verwandte Leitfäden
- Redis-Warteschlange + CaptchaAI
- Batch-CAPTCHA-Lösung
Zuverlässige Warteschlangen mit CaptchaAI und RabbitMQ.
Diskussionen (0)
Beteiligen Sie sich an der Unterhaltung
Melden Sie sich an, um Ihre Meinung zu teilen.
AnmeldenNoch keine Kommentare.