DevOps и масштабирование

Многорегиональная архитектура решения CAPTCHA с помощью CaptchaAI

Развертывание в одном регионе означает единую точку отказа и дополнительную задержку для удаленных целей. Мультирегиональная архитектура позволяет развертывать рабочие CAPTCHA рядом с целевыми сайтами, сокращает время прохождения API туда и обратно и позволяет выдерживать региональные сбои.

Обзор архитектуры

                    [Task Router]
                    (Route53 / Load Balancer)
                    ↙        ↓        ↘
        [US-East]      [EU-West]     [AP-Southeast]
        Workers        Workers        Workers
            ↓              ↓              ↓
        [CaptchaAI API] ← shared API key
            ↓              ↓              ↓
        [Central DB / Queue]
        (Results aggregation)

В каждом регионе работают независимые работники. Все они используют один и тот же ключ API CaptchaAI и отправляют результаты в центральное хранилище.

Когда многорегиональность имеет значение

Ситуация Один регион Мультирегиональный
Целевые сайты в одной стране Достаточный Излишнее убийство
Глобальные целевые сайты Добавленная задержка 100–300 мс Локальная задержка для каждого региона
Требование бесперебойной работы 99,9% Трудно не является универсальным обещанием Естественная избыточность
Резиденция нормативных данных Не могу выполнить Обрабатывать локально
< 1000 задач/hour Отлично Ненужная сложность
> 10 000 задач/hour Пределы масштабирования Распределяет нагрузку

Региональное размещение сотрудников

Python Worker (с учетом региона)

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}

Маршрутизатор задач

Направьте задачи в регион, ближайший к целевому сайту:

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

Настройка инфраструктуры

Терраформированный скелет

# 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 (локальное многорегиональное моделирование)

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);

Стратегия аварийного переключения

При выходе региона из строя перераспределите его задачи:

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

Соображения стоимости

Компонент Фактор стоимости Оптимизация
Рабочие экземпляры Вычисление по регионам Автоматическое масштабирование до 0 в режиме ожидания
Межрегиональная передача данных $0,02/GB между регионами Минимизировать размер полезной нагрузки результата
Очереди SQS Цены по запросу Пакетные сообщения, где это возможно
CaptchaAI API Одинаковая стоимость независимо от региона Нет мультирегиональной премии

CaptchaAI взимает одинаковые тарифы независимо от местоположения работника — стоимость в нескольких регионах относится только к инфраструктуре.

Поиск неисправностей

| Проблема | Причина | Исправить |


Следующие шаги

  • CaptchaAI Quickstart: ваше первое решение CAPTCHA за 5 минут
  • Как решить reCAPTCHA v2 через API: пошаговое руководство
  • Как решить Cloudflare Turnstile через API
  • Как решить GeeTest v3 с помощью API

Политика аварийного переключения

  • Прекратите перенаправление новых задач в регион с ухудшением качества, когда задержка, частота ошибок или работоспособность прокси-сервера превышают порог политики.
  • Сознательно сокращайте работу в процессе, а не резко переключайте каждую очередь и сеанс.
  • Возобновите трафик только после того, как регион встретит те же шлюзы работоспособности, которые использовались перед отработкой отказа.
Комментарии для этой статьи отключены.