DevOps والتوسع

بنية حل اختبار CAPTCHA متعدد المناطق باستخدام CaptchaAI

لا تحتاج كل عملية أتمتة إلى بنية متعددة المناطق. فالجدوى الحقيقية تظهر في ثلاث حالات فقط:

  • أن تكون مواقعك المستهدفة موزّعة على أكثر من قارة.
  • أن يفرض عليك اتفاق مستوى الخدمة استمرارية شبه كاملة.
  • أن يُلزمك الامتثال بإبقاء جزء من المعالجة داخل حدود جغرافية محددة.

خارج هذه الحالات، غالبًا ما يضيف التوزيع الإقليمي عبئًا تشغيليًا يفوق قيمته.

حين تنطبق إحدى هذه الحالات، يصبح توزيع عمال حل CAPTCHA قرب المواقع المستهدفة قرارًا هندسيًا مدروسًا: فهو يقصّر مسار البروكسي إلى الهدف، ويمنحك تكرارًا احتياطيًا يمتص انقطاع أي منطقة، ويحافظ على ثبات الأداء عبر الأسواق. تخيّل فريقًا في الخليج يجمع بيانات من متاجر أوروبية وآسيوية في آن واحد: عامل واحد في منطقة أمريكية سيضيف مئات الميلي ثانية لكل طلب بعيد. الأسطر التالية توضّح كيف تبني هذا النمط فوق CaptchaAI دون تعقيد لا لزوم له.

متى تستحق البنية متعددة المناطق العناء؟

قبل كتابة سطر واحد من Terraform، أجب عن أربعة أسئلة. إن مالت أغلب إجاباتك إلى العمود الأيمن فالتوزيع الإقليمي يخدمك فعلًا؛ وإن مالت إلى العمود الأيسر فابدأ بمنطقة واحدة وراقب قبل أن تتوسّع.

السؤال إذا كانت الإجابة نعم إذا كانت الإجابة لا
هل تستهدف مواقع أو مستخدمين في أكثر من قارة؟ التوزيع الإقليمي منطقي قد تكفي منطقة واحدة
هل لديك متطلبات uptime أو failover صارمة؟ التعدد الإقليمي مفيد أبقِ التصميم أبسط
هل البيانات أو الامتثال يفرضان بقاء بعض الحمل محليًا؟ استخدم مناطق منفصلة بوضوح لا حاجة لهذا التعقيد غالبًا
هل حجم المهام ما زال منخفضًا نسبيًا؟ ابدأ بمنطقة واحدة وراقب لا تتعجل في التوسع

الجدول التالي يترجم هذا القرار إلى مواقف تشغيلية ملموسة، ويقارن ما تقدّمه منطقة واحدة مقابل توزيع متعدد المناطق:

الوضع منطقة واحدة متعدد المناطق
استهداف مواقع داخل بلد واحد كافية مبالغة
مواقع مستهدفة عالمية زمن وصول إضافي 100-300 مللي ثانية زمن وصول محلي لكل منطقة
متطلب توافر عالٍ (وقت تشغيل شبه كامل) يصعب ضمانه تكرار احتياطي طبيعي
إقامة البيانات التنظيمية يتعذّر الامتثال معالجة محلية
<1000 مهمة/الساعة كافية تعقيد بلا داعٍ
>10000 مهمة/الساعة حدود التوسع توزيع للحمل

كيف تبدو البنية على أرض الواقع

                    [Task Router]
                    (Route53 / Load Balancer)
                    ↙        ↓        ↘
        [US-East]      [EU-West]     [AP-Southeast]
        Workers        Workers        Workers
            ↓              ↓              ↓
        [CaptchaAI API] ← shared API key
            ↓              ↓              ↓
        [Central DB / Queue]
        (Results aggregation)
  • عمّال إقليميون: كل منطقة تدير أسطولها الخاص وطابور مهامها المحلي، منفصلة عن غيرها.
  • مفتاح CaptchaAI API مشترك: تستخدمه جميع المناطق دون الحاجة إلى مفاتيح منفصلة.
  • مخزن مركزي: تتجمّع فيه النتائج من كل المناطق للتحليل والمقارنة.

الفصل الكامل بين المناطق هو ما يجعل سقوط إحداها لا يوقف الباقي.

نشر العمال حسب المنطقة

عامل Python واعٍ بالمنطقة

يرسل العامل التالي المهمة إلى CaptchaAI عبر in.php، ثم يستطلع res.php دوريًا حتى تجهز النتيجة. المهم هنا أنه يُرفق بكل نتيجة وسم المنطقة وزمن الحل — وهي بيانات ستحتاجها لاحقًا في المراقبة والمقارنة بين المناطق:

import os
import time
import requests

API_KEY = os.environ["CAPTCHAAI_API_KEY"]
REGION = os.environ.get("WORKER_REGION", "us-east-1")
RESULT_QUEUE_URL = os.environ["RESULT_QUEUE_URL"]


def solve_captcha(task):
    """Solve CAPTCHA and tag with region metadata."""
    start = time.time()

    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": task["method"],
        "googlekey": task["sitekey"],
        "pageurl": task["pageurl"],
        "json": 1
    })
    data = resp.json()

    if data.get("status") != 1:
        return {
            "task_id": task["task_id"],
            "error": data.get("request"),
            "region": REGION
        }

    captcha_id = data["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.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 {
                "task_id": task["task_id"],
                "solution": result["request"],
                "region": REGION,
                "duration": time.time() - start,
                "api_latency_ms": round((time.time() - start) * 1000)
            }
        if result.get("request") != "CAPCHA_NOT_READY":
            return {
                "task_id": task["task_id"],
                "error": result.get("request"),
                "region": REGION
            }

    return {"task_id": task["task_id"], "error": "TIMEOUT", "region": REGION}

موجّه المهام حسب النطاق

يوجّه الموجّه كل مهمة إلى الطابور الأقرب جغرافيًا اعتمادًا على امتداد نطاق الموقع المستهدف (TLD). النطاقات غير المعروفة تذهب إلى المنطقة الافتراضية دون أن تتعطّل المعالجة:

from urllib.parse import urlparse

# Region mapping by target site TLD/domain
REGION_MAP = {
    ".co.uk": "eu-west-1",
    ".de": "eu-central-1",
    ".fr": "eu-west-3",
    ".jp": "ap-northeast-1",
    ".com.au": "ap-southeast-2",
    ".com": "us-east-1",  # Default
}

REGION_QUEUES = {
    "us-east-1": "sqs://captcha-tasks-us-east",
    "eu-west-1": "sqs://captcha-tasks-eu-west",
    "ap-southeast-1": "sqs://captcha-tasks-ap-southeast",
}


def route_task(task):
    """Route task to the closest regional queue."""
    domain = urlparse(task["pageurl"]).netloc

    target_region = "us-east-1"  # Default
    for suffix, region in REGION_MAP.items():
        if domain.endswith(suffix):
            target_region = region
            break

    queue = REGION_QUEUES.get(target_region, REGION_QUEUES["us-east-1"])
    send_to_queue(queue, task)
    return target_region

تجهيز البنية التحتية

هيكل Terraform الأساسي

يعرّف هذا الهيكل قائمة المناطق مرة واحدة، ثم ينشر أسطول عمال وطابور SQS لكل منطقة عبر for_each، مع طابور نتائج مركزي واحد يجمع مخرجات الجميع:

# Define regions
variable "regions" {
  default = ["us-east-1", "eu-west-1", "ap-southeast-1"]
}

# Deploy worker fleet per region
module "captcha_workers" {
  for_each = toset(var.regions)
  source   = "./modules/captcha-worker"

  region              = each.key
  worker_count        = var.workers_per_region
  api_key_secret_arn  = aws_secretsmanager_secret.captchaai_key.arn
  task_queue_arn      = aws_sqs_queue.tasks[each.key].arn
  result_queue_arn    = aws_sqs_queue.results.arn
}

# SQS queue per region for task intake
resource "aws_sqs_queue" "tasks" {
  for_each = toset(var.regions)
  name     = "captcha-tasks-${each.key}"
}

# Central result queue
resource "aws_sqs_queue" "results" {
  name = "captcha-results-central"
}

محاكاة محلية عبر Docker Compose

قبل الدفع إلى السحابة، حاكِ التوزيع على جهازك: ثلاث خدمات عمال بمناطق مختلفة تتشارك خادم Redis واحدًا كوسيط طوابير. هذه الطريقة تكشف أخطاء التوجيه مبكرًا دون تكلفة سحابية:

version: "3.8"
services:
  worker-us:
    build: ./worker
    environment:

      - CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
      - WORKER_REGION=us-east-1
      - TASK_QUEUE=redis://redis:6379/0
    depends_on:

      - redis

  worker-eu:
    build: ./worker
    environment:

      - CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
      - WORKER_REGION=eu-west-1
      - TASK_QUEUE=redis://redis:6379/1

  worker-ap:
    build: ./worker
    environment:

      - CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
      - WORKER_REGION=ap-southeast-1
      - TASK_QUEUE=redis://redis:6379/2

  redis:
    image: redis:7-alpine

مراقبة صحة كل منطقة

لا تكتمل البنية الموزّعة دون رؤية لحظية لحالة كل منطقة. يفحص السكربت التالي نقطة الصحة في كل منطقة كل دقيقة، ويجمع زمن الوصول وعدد العمال النشطين وعمق الطابور في جدول واحد:

JavaScript

const axios = require("axios");

const REGIONS = ["us-east-1", "eu-west-1", "ap-southeast-1"];

async function checkRegionHealth() {
  const health = {};

  for (const region of REGIONS) {
    const endpoint = `https://${region}.workers.example.com/health`;
    try {
      const start = Date.now();
      const resp = await axios.get(endpoint, { timeout: 5000 });
      health[region] = {
        status: "healthy",
        latencyMs: Date.now() - start,
        activeWorkers: resp.data.activeWorkers,
        queueDepth: resp.data.queueDepth,
      };
    } catch (err) {
      health[region] = { status: "unhealthy", error: err.message };
    }
  }

  return health;
}

// Periodic health check
setInterval(async () => {
  const health = await checkRegionHealth();
  console.table(health);
}, 60000);

التحويل التلقائي عند الأعطال

حين تسقط منطقة، يجب إعادة توزيع مهامها فورًا بدل تركها تتراكم. يعمل المنطق التالي على ثلاث خطوات:

  1. حصر المناطق الصحية المتاحة حاليًا.
  2. اختيار أقلها ازدحامًا بديلًا لكل منطقة متعطّلة.
  3. إيقاف التوجيه بالكامل إذا تعطّلت كل المناطق بدل إرسال الحمل إلى فراغ.
def failover_check(region_health):
    """Redirect tasks from unhealthy regions."""
    healthy_regions = [
        r for r, h in region_health.items()
        if h["status"] == "healthy"
    ]

    if not healthy_regions:
        raise RuntimeError("All regions unhealthy")

    redirects = {}
    for region, health in region_health.items():
        if health["status"] == "unhealthy":
            # Pick the healthy region with lowest queue depth
            target = min(
                healthy_regions,
                key=lambda r: region_health[r].get("queue_depth", 0)
            )
            redirects[region] = target
            print(f"Failover: {region} → {target}")

    return redirects

ماذا يكلّفك التوزيع متعدد المناطق؟

الكلفة الإضافية للتوزيع الإقليمي تأتي من البنية التحتية وحدها، لا من CaptchaAI:

مكون عامل التكلفة التحسين
حالات العمال حساب لكل منطقة مقياس تلقائي إلى 0 عندما يكون خاملاً
نقل البيانات عبر المناطق 0.02$/GB بين المناطق تقليل حجم الحمولة الناتجة
طوابير SQS التسعير حسب الطلب رسائل دفعة حيثما أمكن ذلك
واجهة برمجة تطبيقات CaptchaAI نفس التكلفة بغض النظر عن المنطقة لا يوجد قسط متعدد المناطق

خلاصة التكلفة من زاوية CaptchaAI:

  • المحاسبة على عدد الـ Threads المتزامنة لا على كل عملية حل، مع عدد غير محدود من عمليات الحل لكل Thread.
  • السعر نفسه بصرف النظر عن موقع العامل، دون أي زيادة مقابل تشغيل عدة مناطق.
  • العبء الإضافي الوحيد هو حساب العمال ونقل البيانات بين المناطق.

حل المشكلات الشائعة

معظم أعطال البنية الموزّعة ترجع إلى منطق التوجيه أو إلى الخلط بين صحة المنطقة وعمق طابورها:

المشكلة السبب الإجراء
المهام تتجه باستمرار إلى منطقة واحدة منطق التوجيه لا يغطي النطاقات أو suffixes بشكل كافٍ راجع REGION_MAP وسجّل قرارات التوجيه فعليًا
التحول الاحتياطي ينقل الحمل إلى منطقة مزدحمة failover يعتمد على الصحة فقط لا على عمق الطابور اجمع بين health وqueue depth قبل إعادة التوجيه
النتائج تتأخر رغم قرب المنطقة من الهدف الاختناق في الطابور المركزي أو النشر النهائي لا في العامل الإقليمي راقب طبقة التجميع المركزية بدل لوم المنطقة فقط
إدارة الأسرار أصبحت معقدة مفتاح واحد ومخازن متعددة من دون سياسة موحدة استخدم مدير أسرار مركزيًا مع توزيع مضبوط لكل منطقة

أسئلة شائعة

هل يرفع التوزيع متعدد المناطق معدل الحل نفسه؟

لا. معدل حل CAPTCHA يعتمد على نوع الاختبار وعلى خوادم CaptchaAI، لا على موقع عاملك. ما يمنحك إياه التوزيع الإقليمي هو زمن وصول أقل بين العامل وموقعه المستهدف، وتكرار احتياطي يحافظ على استمرارية العمل عند انقطاع منطقة — وكلاهما يحسّن ثبات الأداء العام لا نسبة النجاح لكل مهمة.

متى أضيف منطقة ثانية بدل الاكتفاء بواحدة؟

ابدأ بمنطقة واحدة قرب أكبر تجمّع لمواقعك المستهدفة، وراقب زمن الوصول وعمق الطوابير. أضف الثانية حين يصبح جزء ملموس من أهدافك في قارة أخرى، أو حين يفرض اتفاق مستوى الخدمة تكرارًا احتياطيًا لا تضمنه منطقة واحدة.

كيف يرتبط البروكسي بموقع العامل؟

الفائدة الحقيقية للتوزيع الإقليمي تظهر عند اقتران العامل ببروكسي قريب من الموقع المستهدف، إذ يقصُر المسار الشبكي بينهما. وزّع عمالك قرب مواقعك المستهدفة، لا قرب خوادم CaptchaAI؛ فالأخيرة تخدم الطلبات عالميًا بصرف النظر عن مكان العامل.

كيف أتأكد أن التوزيع الإقليمي خفّض زمن الوصول فعلًا؟

قِس ولا تفترض. يرفق العامل حقلي duration وapi_latency_ms بوسم المنطقة لكل نتيجة؛ اجمعها في مخزنك المركزي وقارن وسيط زمن الوصول لكل منطقة قبل التوزيع وبعده. إن لم ينخفض للأهداف القريبة، فالخلل غالبًا في البروكسي أو منطق التوجيه لا في المنطقة نفسها.


الخطوات التالية

أدلة ذات صلة

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