Пока парсер опрашивает res.php в цикле, поток простаивает в ожидании ответа. AWS SNS (Simple Notification Service) убирает этот цикл: CaptchaAI шлёт результат на callback, тот публикует событие в SNS, а с ним независимо работает любое число подписчиков.
Прежде чем настраивать конвейер, проверьте три вещи:
- У вас есть AWS-аккаунт с правами на создание тем SNS, функций Lambda и очередей SQS.
- API Gateway уже развёрнут и доступен снаружи — CaptchaAI должен дозвониться до вашего callback без VPN и IP-ограничений.
- Есть активный API-ключ CaptchaAI и тип CAPTCHA, который вы уже отправляете через
in.php/res.phpв текущем пайплайне.
Опрос против событийной модели
| Подход | Что держит поток | Когда усложняется |
|---|---|---|
Опрос res.php |
Поток ждёт, пока не придёт ответ, — простой, но неэффективный | На высоком параллелизме растёт число лишних запросов |
| Callback + SNS | Поток свободен сразу после отправки задачи | Нужна Lambda и права на публикацию, зато подписчиков можно добавлять без изменений в парсере |
Архитектура событийного конвейера
[Scraper] → Submit CAPTCHA → [CaptchaAI API]
↓
Solve completes
↓
Callback → [API Gateway + Lambda]
↓
Publish → [SNS Topic]
↓
┌───────────────┼───────────────┐
↓ ↓ ↓
[SQS Queue] [Lambda Logger] [Email Alert]
(result store) (audit trail) (on failure)
SNS работает как разветвитель: результат долетает сразу до всех подписчиков, а callback не знает, кто их слушает.
Совет: если получатель результата всего один, отдельная тема SNS — лишний уровень. Начните с прямой обработки в Lambda-callback и переходите на SNS, когда подписчиков станет больше одного.
Шаг 1. Создайте тему AWS SNS
AWS CLI
aws sns create-topic --name captcha-results --output text
# Returns: arn:aws:sns:us-east-1:123456789:captcha-results
Python (boto3)
import boto3
sns = boto3.client("sns", region_name="us-east-1")
response = sns.create_topic(Name="captcha-results")
topic_arn = response["TopicArn"]
print(f"Topic ARN: {topic_arn}")
Шаг 2. Соберите обработчик обратного вызова
Lambda принимает callback CaptchaAI и публикует результат в SNS.
Важно:
idиcodeв query-параметрах приходят от CaptchaAI без подписи — если роут публичный, добавьте проверку по секретному сегменту URL или отдельному токену внутри Lambda, а не полагайтесь на скрытость адреса.
Python (Lambda Handler)
import json
import os
import boto3
sns = boto3.client("sns")
TOPIC_ARN = os.environ["SNS_TOPIC_ARN"]
def lambda_handler(event, context):
"""Receive CaptchaAI callback and publish to SNS."""
# Parse query parameters from API Gateway
params = event.get("queryStringParameters", {}) or {}
task_id = params.get("id", "")
solution = params.get("code", "")
if not task_id or not solution:
return {"statusCode": 400, "body": "Missing id or code"}
# Publish to SNS
message = {
"task_id": task_id,
"solution": solution,
"status": "solved"
}
sns.publish(
TopicArn=TOPIC_ARN,
Message=json.dumps(message),
Subject="captcha-solved",
MessageAttributes={
"task_id": {
"DataType": "String",
"StringValue": task_id
}
}
)
return {"statusCode": 200, "body": "OK"}
Та же логика на Node.js, если callback-Lambda написана на JavaScript:
JavaScript (Lambda Handler)
const { SNSClient, PublishCommand } = require("@aws-sdk/client-sns");
const sns = new SNSClient({ region: "us-east-1" });
const TOPIC_ARN = process.env.SNS_TOPIC_ARN;
exports.handler = async (event) => {
const params = event.queryStringParameters || {};
const taskId = params.id;
const solution = params.code;
if (!taskId || !solution) {
return { statusCode: 400, body: "Missing id or code" };
}
const message = {
task_id: taskId,
solution: solution,
status: "solved",
};
await sns.send(
new PublishCommand({
TopicArn: TOPIC_ARN,
Message: JSON.stringify(message),
Subject: "captcha-solved",
MessageAttributes: {
task_id: { DataType: "String", StringValue: taskId },
},
})
);
return { statusCode: 200, body: "OK" };
};
Шаг 3. Отправьте CAPTCHA с URL обратного вызова
Укажите в pingback адрес API Gateway — результат придёт сам, без опроса:
Python
import os
import requests
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
CALLBACK_URL = os.environ["CALLBACK_GATEWAY_URL"] # API Gateway URL
def submit_captcha(sitekey, pageurl):
"""Submit CAPTCHA with SNS-backed callback."""
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"pingback": CALLBACK_URL,
"json": 1
})
data = resp.json()
if data.get("status") == 1:
return data["request"] # task_id
raise RuntimeError(f"Submit failed: {data.get('request')}")
Шаг 4. Подключите потребителей к теме
К одной теме можно привязать сразу несколько получателей:
- SQS — очередь, откуда парсер синхронно забирает решения.
- Lambda — асинхронный обработчик для аудита и метрик, ничего не блокирует.
- Email — точечные оповещения о сбоях, без нагрузки на основной поток обработки.
Очередь SQS (хранение результатов)
# Subscribe an SQS queue to receive all results
sqs_arn = "arn:aws:sqs:us-east-1:123456789:captcha-results-queue"
sns.subscribe(
TopicArn=topic_arn,
Protocol="sqs",
Endpoint=sqs_arn
)
Lambda (аудит-лог)
# Subscribe a Lambda for audit logging
lambda_arn = "arn:aws:lambda:us-east-1:123456789:function:captcha-audit-logger"
sns.subscribe(
TopicArn=topic_arn,
Protocol="lambda",
Endpoint=lambda_arn
)
Регистратор аудита не блокирует основной поток: если Lambda-логгер упадёт или притормозит, SQS-очередь с результатами всё равно получит своё сообщение независимо от неё.
Email (оповещения о сбоях)
# Subscribe email for error notifications with filter
sns.subscribe(
TopicArn=topic_arn,
Protocol="email",
Endpoint="[email protected]"
)
Шаг 5. Читайте результаты из SQS
Парсер забирает решения из очереди, а не снова стучится в CaptchaAI:
Python
import json
import boto3
sqs = boto3.client("sqs", region_name="us-east-1")
QUEUE_URL = os.environ["SQS_QUEUE_URL"]
def get_solved_captcha(timeout=30):
"""Wait for a CAPTCHA solution from the SQS queue."""
response = sqs.receive_message(
QueueUrl=QUEUE_URL,
MaxNumberOfMessages=1,
WaitTimeSeconds=min(timeout, 20) # Long polling (max 20s)
)
messages = response.get("Messages", [])
if not messages:
return None
msg = messages[0]
# SNS wraps the message — unwrap it
sns_envelope = json.loads(msg["Body"])
result = json.loads(sns_envelope["Message"])
# Delete message after processing
sqs.delete_message(
QueueUrl=QUEUE_URL,
ReceiptHandle=msg["ReceiptHandle"]
)
return result
Тот же приём на Node.js, если воркер, читающий очередь, написан на JavaScript:
JavaScript
const {
SQSClient,
ReceiveMessageCommand,
DeleteMessageCommand,
} = require("@aws-sdk/client-sqs");
const sqs = new SQSClient({ region: "us-east-1" });
const QUEUE_URL = process.env.SQS_QUEUE_URL;
async function getSolvedCaptcha(timeout = 30) {
const response = await sqs.send(
new ReceiveMessageCommand({
QueueUrl: QUEUE_URL,
MaxNumberOfMessages: 1,
WaitTimeSeconds: Math.min(timeout, 20),
})
);
const messages = response.Messages || [];
if (messages.length === 0) return null;
const msg = messages[0];
const snsEnvelope = JSON.parse(msg.Body);
const result = JSON.parse(snsEnvelope.Message);
await sqs.send(
new DeleteMessageCommand({
QueueUrl: QUEUE_URL,
ReceiptHandle: msg.ReceiptHandle,
})
);
return result;
}
Фильтрация сообщений AWS SNS
Разным подписчикам нужны разные события — в очередь дежурной команды имеет смысл слать только сбои:
# Only send failures to the ops queue
sns.subscribe(
TopicArn=topic_arn,
Protocol="sqs",
Endpoint=failure_queue_arn,
Attributes={
"FilterPolicy": json.dumps({
"status": ["failed", "error"]
})
}
)
Такой фильтр держит очередь дежурной команды чистой: она получает только failed/error, а раздел аналитики может подписаться на весь поток отдельно, не трогая Lambda-callback.
Практический пример развёртывания
Команды с трафиком из России, Беларуси и Казахстана часто размещают тему SNS и Lambda в eu-central-1 — короче сетевой путь до целевых сайтов и меньше промежуточных переходов до callback.
В аудит-логе храните минимум полей — task_id, статус, метку времени — и не добавляйте туда персональные данные заявителя без необходимости. Какой именно состав данных нужен для проверки по 152-ФЗ или GDPR в вашем случае, определяет ваш юрист или DPO; это не заменяет юридическую консультацию, но чем меньше персональных данных в логе, тем проще пройти такую проверку.
Когда не нужен отдельный слой SNS
Пропустите SNS-топик, если:
- у вас всего один потребитель результата, и он и так вызывается напрямую из callback;
- объём — единицы задач в минуту, где выигрыш от развязки не окупает лишнюю Lambda;
- вы уже используете полноценную шину событий (EventBridge, Kafka) и SNS дублировал бы её роль.
Тогда проще обрабатывать callback напрямую и добавить SNS позже, когда подписчиков станет больше одного.
Чек-лист перед продакшеном
- Callback отвечает
200без авторизации на роуте, но проверяет подпись или токен внутри самой Lambda. - На теме настроено разрешение
sns:Publish, и у каждого подписчика есть права на приём сообщений. - Включена проверка
task_idна стороне потребителей — SNS доставляет минимум один раз, дубли возможны. - Задана dead-letter queue (DLQ) для сообщений, которые ни один подписчик не смог обработать.
Типичные проблемы и решения
| Проблема | Причина | Решение |
|---|---|---|
| Callback → 403 | Auth API Gateway блокирует CaptchaAI | Отключите auth, проверяйте токен в Lambda |
| Нет сообщений в SQS | Нет прав sns:Publish |
Добавьте разрешение в политику очереди |
| Дубли результатов | SNS шлёт минимум один раз | Проверяйте task_id перед обработкой |
| Задержка callback | Нет provisioned concurrency | Включите его для Lambda |
Частые вопросы
Сколько потоков CaptchaAI нужно для SNS-конвейера?
Столько же — тариф задаёт параллелизм, а SNS лишь убирает опрос res.php.
Что делать, если Lambda не получает вызов от CaptchaAI?
Проверьте, что pingback доступен извне и отвечает 200 без авторизации — IAM/Cognito-защита роута его заблокирует.
Можно ли сохранить порядок обработки результатов?
Да, через SNS FIFO и SQS FIFO с MessageGroupId = task_id. Для независимых задач хватит обычной темы.
Нужно ли хранить историю решённых CAPTCHA для аудита?
Решает ваша команда — CaptchaAI отвечает только за решение. Берите минимум полей, задайте срок хранения.
Нужно ли платить AWS отдельно за SNS и SQS?
Да. Тариф CaptchaAI считает только потоки и решённые задачи — инфраструктура AWS (SNS, SQS, Lambda, API Gateway) оплачивается отдельно по вашему AWS-счёту и на небольшом трафике обычно укладывается в бесплатный уровень.
Что дальше
Тема SNS и один подписчик — это уже рабочий конвейер. Дальше подключайте остальных получателей и переходите к смежным руководствам: