Polling untuk hasil CAPTCHA mengikat alur dan menciptakan ketergantungan erat antara scraper dan alur penyelesaian. AWS SNS (Layanan Pemberitahuan Sederhana) memisahkan kekhawatiran ini — CaptchaAI mengirimkan hasil ke panggilan balik Anda, yang dipublikasikan ke SNS, dan sejumlah konsumen hilir bereaksi secara independen.
Ikhtisar Arsitektur
[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 menyediakan penyebaran: satu hasil CAPTCHA memicu banyak konsumen tanpa diketahui oleh pengendali panggilan balik.
Langkah 1: Buat Topik 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}")
Langkah 2: Bangun Penerima Panggilan Balik
Fungsi Lambda ini menerima hasil panggilan balik CaptchaAI dan mempublikasikannya ke SNS.
Python (Penanganan Lambda)
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"}
JavaScript (Penanganan Lambda)
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" };
};
Langkah 3: Kirim CAPTCHA dengan URL Panggilan Balik
Arahkan pingback CaptchaAI ke titik akhir API Gateway Anda:
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')}")
Langkah 4: Berlangganan Konsumen
Antrean SQS (Penyimpanan Hasil)
# 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 (Pencatat Audit)
# 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
)
Email (Peringatan Kegagalan)
# Subscribe email for error notifications with filter
sns.subscribe(
TopicArn=topic_arn,
Protocol="email",
Endpoint="ops@example.com"
)
Langkah 5: Konsumsi Hasil dari SQS
Scraper Anda membaca solusi dari SQS alih-alih melakukan polling 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
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;
}
Penyaringan Pesan SNS
Memberikan hasil yang berbeda ke konsumen yang berbeda:
# 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"]
})
}
)
Pemecahan Masalah
| Masalah | Penyebab | Solusi |
|---|---|---|
| Callback mengembalikan 403 | Autentikasi API Gateway memblokir CaptchaAI | Nonaktifkan autentikasi pada rute callback; gunakan validasi berbasis token |
| Pesan SQS tidak sampai | Izin SNS → SQS tidak ada | Tambahkan izin sns:Publish ke kebijakan antrean SQS |
| Hasil duplikat diproses | SNS mengirimkan at-least-once | Terapkan idempotency – periksa task_id sebelum diproses |
| Lambda cold start menunda callback | Provisioned concurrency tidak disetel | Aktifkan provisioned concurrency untuk callback Lambda |
Pertanyaan Umum
Mengapa menggunakan SNS alih-alih memproses hasil secara langsung di callback Lambda?
SNS memisahkan pengendali panggilan balik dari logika hilir. Anda dapat menambahkan konsumen baru (logging, alerting, analitik) tanpa mengubah callback Lambda. Panggilan balik tetap sederhana dan cepat.
Berapa latensi tambahan dari lapisan SNS?
SNS menambahkan 10–50 ms per pesan. Karena penyelesaian CAPTCHA memerlukan waktu 5–30 detik, overhead ini dapat diabaikan.
Bisakah saya menggunakan SNS FIFO untuk pemrosesan pesanan?
Ya. Gunakan topik SNS FIFO dengan antrian SQS FIFO jika Anda memerlukan hasil yang diurutkan. Atur MessageGroupId ke ID tugas untuk pengurutan per tugas.
Artikel Terkait
- Membangun Pipeline CAPTCHA Klien CaptchaAI
- Keamanan Webhook: Memvalidasi Callback
- Integrasi Serverless AWS Lambda + CaptchaAI
Langkah Selanjutnya
Bangun penyelesaian CAPTCHA berbasis event — dapatkan kunci API CaptchaAI Anda dan hubungkan ke alur event AWS Anda.