Lệnh gọi lại (pingback) loại bỏ việc thăm dò ý kiến, nhưng chúng đưa ra một chế độ lỗi mới: điều gì xảy ra khi máy chủ của bạn ngừng hoạt động, trả về lỗi hoặc hết thời gian chờ khi CaptchaAI cố gắng cung cấp kết quả? Hướng dẫn này bao gồm các mẫu để xử lý lỗi gọi lại mà không làm mất giải pháp CAPTCHA.
Điều gì có thể sai
| Chế độ lỗi | Triệu chứng | kết quả |
|---|---|---|
| Máy chủ ngừng hoạt động | CaptchaAI bị từ chối kết nối | Giải pháp chưa được giao |
| Máy chủ trả về 5xx | CaptchaAI nhận được phản hồi lỗi | Có thể không thử lại (tùy thuộc vào việc triển khai) |
| Hết thời gian chờ mạng | Kết nối CaptchaAI bị treo | Giải pháp có thể bị mất |
| Trình xử lý gặp sự cố | Yêu cầu được chấp nhận nhưng kết quả không được lưu trữ | Giải pháp âm thầm bị bỏ rơi |
Giải pháp: không bao giờ chỉ dựa vào lệnh gọi lại. Luôn có một dự phòng.
Mẫu 1: Gọi lại + Thăm dò dự phòng
Cách tiếp cận đáng tin cậy nhất — chấp nhận lệnh gọi lại khi chúng đến nhưng thăm dò bất kỳ nhiệm vụ nào không nhận được lệnh gọi lại trong thời gian chờ.
Python
import os
import time
import threading
import requests
from flask import Flask, request
app = Flask(__name__)
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
# Track task state
pending_tasks = {} # task_id -> {"submitted_at": timestamp, "status": "pending"}
results = {}
lock = threading.Lock()
def submit_captcha(sitekey, pageurl, callback_url):
"""Submit with callback, but track for fallback polling."""
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:
task_id = data["request"]
with lock:
pending_tasks[task_id] = {
"submitted_at": time.time(),
"status": "pending"
}
return task_id
return None
@app.route("/callback")
def captcha_callback():
"""Primary result delivery — CaptchaAI sends results here."""
task_id = request.args.get("id")
solution = request.args.get("code")
with lock:
results[task_id] = solution
pending_tasks.pop(task_id, None)
return "OK", 200
def fallback_poller():
"""Poll for any tasks that missed their callback."""
while True:
time.sleep(30) # Check every 30 seconds
with lock:
stale_tasks = [
tid for tid, info in pending_tasks.items()
if time.time() - info["submitted_at"] > 120 # 2 min callback timeout
and info["status"] == "pending"
]
for task_id in stale_tasks:
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1
})
data = resp.json()
if data.get("status") == 1:
with lock:
results[task_id] = data["request"]
pending_tasks.pop(task_id, None)
print(f"Fallback poll recovered: {task_id}")
elif data.get("request") != "CAPCHA_NOT_READY":
# Permanent error — remove from pending
with lock:
pending_tasks.pop(task_id, None)
print(f"Task failed: {task_id} — {data.get('request')}")
# Start fallback poller in background
poller_thread = threading.Thread(target=fallback_poller, daemon=True)
poller_thread.start()
JavaScript
const express = require("express");
const axios = require("axios");
const app = express();
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const pendingTasks = new Map(); // taskId -> { submittedAt, status }
const results = new Map();
async function submitCaptcha(sitekey, pageurl, callbackUrl) {
const resp = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageurl,
pingback: callbackUrl,
json: 1,
},
});
if (resp.data.status === 1) {
const taskId = resp.data.request;
pendingTasks.set(taskId, {
submittedAt: Date.now(),
status: "pending",
});
return taskId;
}
return null;
}
// Primary callback endpoint
app.get("/callback", (req, res) => {
const taskId = req.query.id;
const solution = req.query.code;
results.set(taskId, solution);
pendingTasks.delete(taskId);
res.sendStatus(200);
});
// Fallback poller
setInterval(async () => {
const now = Date.now();
const staleTasks = [];
for (const [taskId, info] of pendingTasks) {
if (now - info.submittedAt > 120000 && info.status === "pending") {
staleTasks.push(taskId);
}
}
for (const taskId of staleTasks) {
try {
const resp = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId, json: 1 },
});
if (resp.data.status === 1) {
results.set(taskId, resp.data.request);
pendingTasks.delete(taskId);
console.log(`Fallback recovered: ${taskId}`);
} else if (resp.data.request !== "CAPCHA_NOT_READY") {
pendingTasks.delete(taskId);
console.log(`Task failed: ${taskId} — ${resp.data.request}`);
}
} catch (err) {
console.error(`Poll error for ${taskId}: ${err.message}`);
}
}
}, 30000);
app.listen(3000);
Mẫu 2: Hàng đợi thư chết
Khi trình xử lý gọi lại của bạn xử lý một kết quả nhưng gặp lỗi (cơ sở dữ liệu ngừng hoạt động, xác thực không thành công), hãy chuyển vấn đề sang hàng đợi thư chết thay vì mất dữ liệu.
Python
import json
import os
import time
from pathlib import Path
DEAD_LETTER_DIR = Path("dead_letter")
DEAD_LETTER_DIR.mkdir(exist_ok=True)
@app.route("/callback")
def captcha_callback_with_dlq():
task_id = request.args.get("id")
solution = request.args.get("code")
try:
# Attempt normal processing
store_result(task_id, solution)
return "OK", 200
except Exception as e:
# Processing failed — save to dead-letter queue
dead_letter = {
"task_id": task_id,
"solution": solution,
"error": str(e),
"received_at": time.time()
}
dlq_path = DEAD_LETTER_DIR / f"{task_id}.json"
dlq_path.write_text(json.dumps(dead_letter))
print(f"DLQ: {task_id} — {e}")
return "OK", 200 # Still return 200 to CaptchaAI
def reprocess_dead_letters():
"""Retry processing dead-letter items."""
for dlq_file in DEAD_LETTER_DIR.glob("*.json"):
item = json.loads(dlq_file.read_text())
try:
store_result(item["task_id"], item["solution"])
dlq_file.unlink() # Remove after successful processing
print(f"DLQ reprocessed: {item['task_id']}")
except Exception:
pass # Leave in DLQ for next retry
JavaScript
const fs = require("fs");
const path = require("path");
const DLQ_DIR = path.join(__dirname, "dead_letter");
if (!fs.existsSync(DLQ_DIR)) fs.mkdirSync(DLQ_DIR);
app.get("/callback-dlq", (req, res) => {
const taskId = req.query.id;
const solution = req.query.code;
try {
storeResult(taskId, solution);
res.sendStatus(200);
} catch (err) {
// Save to dead-letter queue
const deadLetter = {
task_id: taskId,
solution: solution,
error: err.message,
received_at: Date.now(),
};
fs.writeFileSync(
path.join(DLQ_DIR, `${taskId}.json`),
JSON.stringify(deadLetter)
);
console.log(`DLQ: ${taskId} — ${err.message}`);
res.sendStatus(200); // Still acknowledge to CaptchaAI
}
});
function reprocessDeadLetters() {
const files = fs.readdirSync(DLQ_DIR).filter((f) => f.endsWith(".json"));
for (const file of files) {
const filePath = path.join(DLQ_DIR, file);
const item = JSON.parse(fs.readFileSync(filePath, "utf8"));
try {
storeResult(item.task_id, item.solution);
fs.unlinkSync(filePath);
console.log(`DLQ reprocessed: ${item.task_id}`);
} catch (err) {
// Leave in DLQ
}
}
}
// Retry DLQ every 5 minutes
setInterval(reprocessDeadLetters, 300000);
Mẫu 3: Trình xử lý gọi lại bình thường
Cuộc gọi lại có thể được gửi nhiều lần. Làm cho trình xử lý của bạn trở nên bình thường:
@app.route("/callback")
def idempotent_callback():
task_id = request.args.get("id")
solution = request.args.get("code")
with lock:
# Only process if not already handled
if task_id in results:
return "OK", 200 # Already processed — skip silently
results[task_id] = solution
pending_tasks.pop(task_id, None)
return "OK", 200
Ma trận quyết định: Nên sử dụng mẫu nào
| Kịch bản | Mẫu đẹp nhất |
|---|---|
| Khối lượng thấp, thỉnh thoảng ngừng hoạt động | Gọi lại + Thăm dò dự phòng |
| Khối lượng lớn, có thể mất cơ sở dữ liệu | Hàng đợi thư chết |
| Nhiều người tiêu dùng có thể xử lý cùng một kết quả | Trình xử lý bình thường |
| Hệ thống sản xuất với SLA | Cả ba kết hợp |
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Trình thăm dò dự phòng tìm thấy các nhiệm vụ đã được phân phối | Cuộc đua giữa cuộc gọi lại và cuộc thăm dò ý kiến | Thêm kiểm tra idempotency - bỏ qua nếu đã có kết quả |
| DLQ phát triển mà không được xử lý | Bộ xử lý lại không chạy hoặc bị lỗi | Kiểm tra nhật ký bộ xử lý lại; đảm bảo vấn đề cơ bản (DB) được khắc phục |
| Gọi lại trả về 200 nhưng kết quả bị mất | Trình xử lý gặp sự cố sau khi phản hồi được gửi | Xử lý trước khi phản hồi hoặc sử dụng mẫu DLQ |
| Quá nhiều yêu cầu thăm dò dự phòng | Quá nhiều nhiệm vụ cũ | Tăng ngưỡng thời gian chờ gọi lại; kiểm tra thời gian hoạt động của máy chủ |
Câu hỏi thường gặp
Tôi có nên luôn trả lại 200 cho các lệnh gọi lại CaptchaAI không?
Vâng. Việc trả lại mã lỗi (4xx/5xx) không giúp ích được gì — CaptchaAI không thể thử lại lệnh gọi lại. Luôn chấp nhận phân phối (200 OK) và xử lý lỗi nội bộ bằng DLQ hoặc bỏ phiếu dự phòng.
Tôi nên đợi bao lâu trước khi bỏ phiếu dự phòng?
Đợi ít nhất 120 giây sau khi gửi. Hầu hết CAPTCHA đều giải quyết trong vòng 10–60 giây, cộng với độ trễ mạng khi gửi lệnh gọi lại. Hai phút là đủ thời gian để cuộc gọi lại đến.
Tôi có thể tắt tính năng gọi lại và chỉ thăm dò ý kiến không?
Có — chỉ đơn giản là không bao gồm tham số pingback. Nhưng các lệnh gọi lại làm giảm đáng kể các lệnh gọi API trên quy mô lớn (2 lệnh gọi cho mỗi tác vụ thay vì hơn 10 yêu cầu thăm dò ý kiến).
bài viết liên quan
- Python giải quyết các mẫu lỗi thử lại
- Xác thực gọi lại bảo mật Captchaai Webhook
- Tham khảo mã lỗi Captchaai
Các bước tiếp theo
Xây dựng quy trình xử lý gọi lại CAPTCHA đáng tin cậy —lấy khóa API CaptchaAI của bạnvà thực hiện các mô hình kiên cường này.
Hướng dẫn liên quan:
- URL gọi lại và hướng dẫn Webhook
- Các mẫu thông báo tác vụ Pingback
- Bảo mật Webhook: Xác thực lệnh gọi lại