Khi giải CAPTCHA không thành công sau tất cả các lần thử lại, dữ liệu tác vụ sẽ bị mất trừ khi bạn chụp được nó. Hàng đợi thư chết (DLQ) lưu trữ các tác vụ không thành công để thử lại, phân tích hoặc cảnh báo sau này — do đó, không có công việc nào bị âm thầm bỏ qua.
Khi nhiệm vụ thất bại
Những lý do phổ biến khiến tác vụ CAPTCHA kết thúc trong DLQ:
ERROR_CAPTCHA_UNSOLVABLE— Người giải không thể hoàn thành thử tháchERROR_NO_SLOT_AVAILABLE– Tất cả công nhân đều bận rộn, thử lại đều kiệt sức- Hết thời gian - Bộ giải không trả về kết quả trong thời hạn
- Lỗi mạng - Kết nối bị rớt trong quá trình bỏ phiếu
Nếu không có DLQ, những lỗi này sẽ tạo ra dòng nhật ký và bị lãng quên.
Python: DLQ trong bộ nhớ có thử lại
import time
import json
import requests
from collections import deque
from dataclasses import dataclass, asdict
from typing import Optional
API_KEY = "YOUR_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
@dataclass
class FailedTask:
sitekey: str
page_url: str
error: str
attempts: int
timestamp: float
task_id: Optional[str] = None
class DeadLetterQueue:
def __init__(self, max_size=1000, max_retries=3):
self._queue = deque(maxlen=max_size)
self.max_retries = max_retries
def push(self, task: FailedTask):
self._queue.append(task)
print(f"[dlq] Added: {task.error} (attempts: {task.attempts})")
def pop(self) -> Optional[FailedTask]:
return self._queue.popleft() if self._queue else None
def size(self) -> int:
return len(self._queue)
def peek_all(self) -> list:
return [asdict(t) for t in self._queue]
def export_json(self, path: str):
with open(path, "w") as f:
json.dump(self.peek_all(), f, indent=2)
print(f"[dlq] Exported {self.size()} tasks to {path}")
dlq = DeadLetterQueue(max_retries=3)
def solve_captcha(sitekey, page_url, max_retries=3):
for attempt in range(max_retries + 1):
try:
resp = requests.post(SUBMIT_URL, data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": "1",
}, timeout=15)
data = resp.json()
if data["status"] != 1:
raise Exception(data["request"])
task_id = data["request"]
for _ in range(24):
time.sleep(5)
poll = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
}, timeout=15).json()
if poll["status"] == 1:
return poll["request"]
if poll["request"] != "CAPCHA_NOT_READY":
raise Exception(poll["request"])
raise TimeoutError(f"Task {task_id} timed out")
except Exception as e:
if attempt == max_retries:
dlq.push(FailedTask(
sitekey=sitekey,
page_url=page_url,
error=str(e),
attempts=attempt + 1,
timestamp=time.time(),
))
return None
time.sleep(2 ** attempt)
return None
# Process a batch
urls = [f"https://example.com/page/{i}" for i in range(5)]
for url in urls:
token = solve_captcha("6Le-SITEKEY", url)
if token:
print(f"Solved: {token[:40]}...")
print(f"\nDLQ size: {dlq.size()}")
Sản lượng dự kiến:
Solved: 03AGdBq26ZfPxL...
Solved: 03AGdBq27AbCdE...
[dlq] Added: ERROR_CAPTCHA_UNSOLVABLE (attempts: 4)
Solved: 03AGdBq28FgHiJ...
[dlq] Added: Task 71823460 timed out (attempts: 4)
DLQ size: 2
Đang thử lại từ DLQ
def retry_dlq(dlq: DeadLetterQueue, max_retries=2):
retried = 0
recovered = 0
while dlq.size() > 0:
task = dlq.pop()
if task.attempts >= dlq.max_retries + max_retries:
print(f"[dlq] Permanently failed: {task.sitekey} — {task.error}")
continue
retried += 1
token = solve_captcha(
task.sitekey, task.page_url, max_retries=max_retries
)
if token:
recovered += 1
print(f"[dlq-retry] Recovered: {token[:40]}...")
print(f"[dlq] Retried: {retried}, Recovered: {recovered}")
# Run DLQ retry after main batch
retry_dlq(dlq)
JavaScript: DLQ với tính bền vững của tệp
const fs = require('fs');
const axios = require('axios');
const API_KEY = 'YOUR_API_KEY';
const DLQ_FILE = './captcha-dlq.json';
class DeadLetterQueue {
constructor(maxRetries = 3) {
this.maxRetries = maxRetries;
this.queue = this._load();
}
push(task) {
this.queue.push({
...task,
timestamp: Date.now(),
});
this._save();
console.log(`[dlq] Added: ${task.error} (attempts: ${task.attempts})`);
}
pop() {
const task = this.queue.shift();
if (task) this._save();
return task || null;
}
size() {
return this.queue.length;
}
_load() {
try {
return JSON.parse(fs.readFileSync(DLQ_FILE, 'utf8'));
} catch {
return [];
}
}
_save() {
fs.writeFileSync(DLQ_FILE, JSON.stringify(this.queue, null, 2));
}
}
const dlq = new DeadLetterQueue(3);
async function solveCaptcha(sitekey, pageurl, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const submit = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: { key: API_KEY, method: 'userrecaptcha', googlekey: sitekey, pageurl, json: 1 }
});
if (submit.data.status !== 1) throw new Error(submit.data.request);
const taskId = submit.data.request;
for (let i = 0; i < 24; i++) {
await new Promise(r => setTimeout(r, 5000));
const poll = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: taskId, json: 1 }
});
if (poll.data.status === 1) return poll.data.request;
if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
}
throw new Error(`Task ${taskId} timed out`);
} catch (err) {
if (attempt === maxRetries) {
dlq.push({ sitekey, pageurl, error: err.message, attempts: attempt + 1 });
return null;
}
await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
}
}
}
// Process tasks
(async () => {
for (let i = 0; i < 5; i++) {
const token = await solveCaptcha('6Le-SITEKEY', `https://example.com/page/${i}`);
if (token) console.log(`Solved: ${token.substring(0, 40)}...`);
}
console.log(`DLQ size: ${dlq.size()}`);
})();
phân tích DLQ
Xuất và phân tích các tác vụ thất bại để tìm mẫu:
# Export DLQ for analysis
dlq.export_json("failed-tasks.json")
# Analyze error distribution
from collections import Counter
errors = Counter(t["error"] for t in dlq.peek_all())
for error, count in errors.most_common():
print(f" {error}: {count}")
Sử dụng dữ liệu này để:
- Xác định các khóa trang web liên tục bị lỗi †' kiểm tra xem các tham số có chính xác không
- Thời gian chờ tại chỗ trong những giờ cụ thể → tương quan với tải API
- Tìm lỗi mạng †' kiểm tra tình trạng proxy
Chính sách phát lại thư chết
- Giữ bối cảnh yêu cầu ban đầu, lý do thất bại và số lần thử lại cùng nhau để việc phát lại luôn an toàn và có thể gỡ lỗi.
- Xác định lỗi nào có thể được phát lại tự động và lỗi nào phải chờ người vận hành xem xét.
- Coi thông lượng phát lại, tuổi hàng đợi và tỷ lệ lỗi lặp lại là tín hiệu vận hành hạng nhất.
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| DLQ phát triển vô hạn | Không xử lý lần thử lại | Lên lịch xả DLQ định kỳ với retry_dlq() |
| Nhiệm vụ tương tự được thử lại mãi mãi | Không có giới hạn số lần thử tối đa | Kiểm tra task.attempts trước khi xếp hàng lại |
| Tệp DLQ bị hỏng | Viết đồng thời | Sử dụng khóa tệp hoặc chuyển sang Redis/database |
| Mất nhiệm vụ khi gặp sự cố | Chỉ DLQ trong bộ nhớ | Sử dụng DLQ dựa trên tệp hoặc được Redis hỗ trợ |
Câu hỏi thường gặp
Tôi nên sử dụng DLQ trong bộ nhớ hay liên tục?
Sử dụng trong bộ nhớ cho các tập lệnh chạy ngắn. Sử dụng dựa trên tệp hoặc được Redis hỗ trợ cho các dịch vụ chạy lâu trong đó việc khởi động lại quy trình sẽ làm mất các tác vụ được xếp hàng đợi.
Khi nào tôi nên loại bỏ vĩnh viễn một nhiệm vụ?
Sau 2-3 lần thử lại DLQ (ngoài số lần thử lại ban đầu). Nếu một tác vụ thất bại tổng cộng hơn 6 lần, các tham số có thể sai - hãy ghi lại nó và tiếp tục.
Tôi có thể kết hợp điều này với mẫu ngắt mạch không?
Đúng. Bộ ngắt mạch ngăn chặn việc gửi yêu cầu trong thời gian ngừng hoạt động và DLQ ghi lại mọi tác vụ bị lỗi trước khi ngắt mạch. Nhìn thấyMẫu ngắt mạch.
Không bao giờ mất nhiệm vụ CAPTCHA thất bại nữa với CaptchaAI
Nhận khóa API của bạn tạicaptchaai.com.