دروس API

قائمة انتظار الرسائل الميتة لمهام اختبار CAPTCHA الفاشلة

عندما يفشل حل اختبار CAPTCHA بعد كل عمليات إعادة المحاولة، يتم فقدان بيانات المهمة إلا إذا قمت بالتقاطها. تقوم قائمة انتظار الأحرف الميتة (DLQ) بتخزين المهام الفاشلة لإعادة المحاولة أو التحليل أو التنبيه لاحقًا - لذلك لا يتم إسقاط أي عمل بصمت.


عندما تفشل المهام

الأسباب الشائعة التي تؤدي إلى انتهاء مهمة CAPTCHA في DLQ:

  • ERROR_CAPTCHA_UNSOLVABLE - لم يتمكن القائم بالحل من إكمال التحدي
  • ERROR_NO_SLOT_AVAILABLE - جميع العمال مشغولون، ويتم إعادة المحاولة مرهقة
  • المهلة – لم يُرجع Solver نتيجة خلال الموعد النهائي
  • أخطاء في الشبكة – انقطع الاتصال أثناء الاستقصاء

وبدون DLQ، تنتج حالات الفشل هذه سطر سجل ويتم نسيانها.


Python: DLQ في الذاكرة مع إعادة المحاولة

import time
import json
import requests
from collections import deque
from dataclasses import dataclass, asdict
from typing import Optional

API_KEY = "YOUR_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"


@dataclass
class FailedTask:
    sitekey: str
    page_url: str
    error: str
    attempts: int
    timestamp: float
    task_id: Optional[str] = None


class DeadLetterQueue:
    def __init__(self, max_size=1000, max_retries=3):
        self._queue = deque(maxlen=max_size)
        self.max_retries = max_retries

    def push(self, task: FailedTask):
        self._queue.append(task)
        print(f"[dlq] Added: {task.error} (attempts: {task.attempts})")

    def pop(self) -> Optional[FailedTask]:
        return self._queue.popleft() if self._queue else None

    def size(self) -> int:
        return len(self._queue)

    def peek_all(self) -> list:
        return [asdict(t) for t in self._queue]

    def export_json(self, path: str):
        with open(path, "w") as f:
            json.dump(self.peek_all(), f, indent=2)
        print(f"[dlq] Exported {self.size()} tasks to {path}")


dlq = DeadLetterQueue(max_retries=3)


def solve_captcha(sitekey, page_url, max_retries=3):
    for attempt in range(max_retries + 1):
        try:
            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(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(poll["request"])

            raise TimeoutError(f"Task {task_id} timed out")

        except Exception as e:
            if attempt == max_retries:
                dlq.push(FailedTask(
                    sitekey=sitekey,
                    page_url=page_url,
                    error=str(e),
                    attempts=attempt + 1,
                    timestamp=time.time(),
                ))
                return None
            time.sleep(2 ** attempt)

    return None


# Process a batch
urls = [f"https://example.com/page/{i}" for i in range(5)]
for url in urls:
    token = solve_captcha("6Le-SITEKEY", url)
    if token:
        print(f"Solved: {token[:40]}...")

print(f"\nDLQ size: {dlq.size()}")

الناتج المتوقع:

Solved: 03AGdBq26ZfPxL...
Solved: 03AGdBq27AbCdE...
[dlq] Added: ERROR_CAPTCHA_UNSOLVABLE (attempts: 4)
Solved: 03AGdBq28FgHiJ...
[dlq] Added: Task 71823460 timed out (attempts: 4)

DLQ size: 2

إعادة المحاولة من DLQ

def retry_dlq(dlq: DeadLetterQueue, max_retries=2):
    retried = 0
    recovered = 0

    while dlq.size() > 0:
        task = dlq.pop()
        if task.attempts >= dlq.max_retries + max_retries:
            print(f"[dlq] Permanently failed: {task.sitekey} — {task.error}")
            continue

        retried += 1
        token = solve_captcha(
            task.sitekey, task.page_url, max_retries=max_retries
        )
        if token:
            recovered += 1
            print(f"[dlq-retry] Recovered: {token[:40]}...")

    print(f"[dlq] Retried: {retried}, Recovered: {recovered}")

# Run DLQ retry after main batch
retry_dlq(dlq)

JavaScript: DLQ مع ثبات الملف

const fs = require('fs');
const axios = require('axios');

const API_KEY = 'YOUR_API_KEY';
const DLQ_FILE = './captcha-dlq.json';

class DeadLetterQueue {
  constructor(maxRetries = 3) {
    this.maxRetries = maxRetries;
    this.queue = this._load();
  }

  push(task) {
    this.queue.push({
      ...task,
      timestamp: Date.now(),
    });
    this._save();
    console.log(`[dlq] Added: ${task.error} (attempts: ${task.attempts})`);
  }

  pop() {
    const task = this.queue.shift();
    if (task) this._save();
    return task || null;
  }

  size() {
    return this.queue.length;
  }

  _load() {
    try {
      return JSON.parse(fs.readFileSync(DLQ_FILE, 'utf8'));
    } catch {
      return [];
    }
  }

  _save() {
    fs.writeFileSync(DLQ_FILE, JSON.stringify(this.queue, null, 2));
  }
}

const dlq = new DeadLetterQueue(3);

async function solveCaptcha(sitekey, pageurl, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      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(`Task ${taskId} timed out`);
    } catch (err) {
      if (attempt === maxRetries) {
        dlq.push({ sitekey, pageurl, error: err.message, attempts: attempt + 1 });
        return null;
      }
      await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
    }
  }
}

// Process tasks
(async () => {
  for (let i = 0; i < 5; i++) {
    const token = await solveCaptcha('6Le-SITEKEY', `https://example.com/page/${i}`);
    if (token) console.log(`Solved: ${token.substring(0, 40)}...`);
  }
  console.log(`DLQ size: ${dlq.size()}`);
})();

تحليل DLQ

تصدير وتحليل المهام الفاشلة للعثور على الأنماط:

# Export DLQ for analysis
dlq.export_json("failed-tasks.json")

# Analyze error distribution
from collections import Counter
errors = Counter(t["error"] for t in dlq.peek_all())
for error, count in errors.most_common():
    print(f"  {error}: {count}")

استخدم هذه البيانات من أجل:

  • حدد مفاتيح الموقع التي تفشل باستمرار - وتحقق مما إذا كانت المعلمات صحيحة
  • ترتبط المهلات الموضعية خلال ساعات محددة - مع تحميل واجهة برمجة التطبيقات
  • ابحث عن أخطاء الشبكة - تحقق من صحة الوكيل

استكشاف الأخطاء وإصلاحها

مشكلة السبب إصلاح
DLQ ينمو إلى أجل غير مسمى لا تتم معالجة المحاولات جدولة تصريف DLQ الدوري باستخدام retry_dlq()
تمت إعادة محاولة نفس المهمة إلى الأبد لا يوجد حد أقصى للمحاولة تحقق من task.attempts قبل إعادة الانتظار
ملف DLQ تالف يكتب المتزامنة استخدم قفل الملف أو قم بالتبديل إلى Redis/database
المهام المفقودة عند التعطل DLQ في الذاكرة فقط استخدم DLQ المستند إلى الملف أو المدعوم من Redis

الأسئلة الشائعة

هل يجب أن أستخدم DLQ داخل الذاكرة أم مستمرًا؟

استخدم الذاكرة الداخلية للبرامج النصية قصيرة المدى. استخدم الملفات المستندة إلى الملفات أو المدعومة من Redis للخدمات طويلة الأمد حيث قد تؤدي إعادة تشغيل العملية إلى فقدان المهام الموضوعة في قائمة الانتظار.

متى يجب أن أتجاهل مهمة بشكل دائم؟

بعد 2-3 عمليات إعادة محاولة DLQ (فوق عمليات إعادة المحاولة الأصلية). إذا فشلت المهمة أكثر من 6 مرات، فمن المحتمل أن تكون المعلمات خاطئة - قم بتسجيلها والمضي قدمًا.

هل يمكنني دمج هذا مع نمط قاطع الدائرة؟

نعم. يمنع قاطع الدائرة إرسال الطلبات أثناء انقطاع التيار، ويلتقط DLQ أي مهام تفشل قبل رحلات الدائرة. يرىنمط قواطع الدائرة.


لا تفقد أبدًا مهمة CAPTCHA الفاشلة مرة أخرى مع CaptchaAI

احصل على مفتاح API الخاص بك علىcaptchaai.com.


أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.