Khi API giải CAPTCHA chậm hoặc trả về lỗi, việc tiếp tục gửi yêu cầu sẽ gây lãng phí thời gian và tiền bạc. Mẫu ngắt mạch dừng gọi API bị lỗi, chờ khôi phục và tự động tiếp tục - ngăn ngừa lỗi xếp tầng trong đường ống của bạn.
Bộ ngắt mạch hoạt động như thế nào
Ba trạng thái:
- Đã đóng - Hoạt động bình thường. Yêu cầu đi qua. Thất bại được tính.
- Mở — Quá nhiều lỗi. Mọi yêu cầu đều bị từ chối ngay lập tức nếu không gọi API.
- Nửa mở – Sau một khoảng thời gian hồi chiêu, một yêu cầu thử nghiệm được phép thực hiện. Nếu thành công thì mạch sẽ đóng lại. Nếu thất bại, mạch sẽ mở lại.
Triển khai Python
import time
import threading
import requests
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
API_KEY = "YOUR_API_KEY"
class CircuitBreaker:
def __init__(self, failure_threshold=5, recovery_timeout=60):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.failure_count = 0
self.last_failure_time = 0
self.state = "closed" # closed, open, half-open
self._lock = threading.Lock()
def call(self, func, *args, **kwargs):
with self._lock:
if self.state == "open":
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = "half-open"
print("[circuit] State: half-open — testing one request")
else:
remaining = self.recovery_timeout - (
time.time() - self.last_failure_time
)
raise CircuitOpenError(
f"Circuit open — retry in {remaining:.0f}s"
)
try:
result = func(*args, **kwargs)
with self._lock:
self.failure_count = 0
if self.state == "half-open":
print("[circuit] State: closed — API recovered")
self.state = "closed"
return result
except Exception as e:
with self._lock:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "open"
print(
f"[circuit] State: open — "
f"{self.failure_count} failures"
)
raise
class CircuitOpenError(Exception):
pass
def solve_captcha(sitekey, page_url):
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(f"Submit error: {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(f"Poll error: {poll['request']}")
raise TimeoutError(f"Task {task_id} timed out")
# Usage
breaker = CircuitBreaker(failure_threshold=3, recovery_timeout=30)
for i in range(10):
try:
token = breaker.call(
solve_captcha, "6Le-SITEKEY", "https://example.com"
)
print(f"[task-{i}] Solved: {token[:40]}...")
except CircuitOpenError as e:
print(f"[task-{i}] Skipped: {e}")
except Exception as e:
print(f"[task-{i}] Failed: {e}")
Sản lượng dự kiến:
[task-0] Solved: 03AGdBq26ZfPxL...
[task-1] Solved: 03AGdBq27AbCdE...
[task-2] Failed: Submit error: ERROR_NO_SLOT_AVAILABLE
[task-3] Failed: Submit error: ERROR_NO_SLOT_AVAILABLE
[task-4] Failed: Submit error: ERROR_NO_SLOT_AVAILABLE
[circuit] State: open — 3 failures
[task-5] Skipped: Circuit open — retry in 28s
[task-6] Skipped: Circuit open — retry in 25s
...
[circuit] State: half-open — testing one request
[task-8] Solved: 03AGdBq28FgHiJ...
[circuit] State: closed — API recovered
Triển khai JavaScript
class CircuitBreaker {
constructor(options = {}) {
this.failureThreshold = options.failureThreshold || 5;
this.recoveryTimeout = options.recoveryTimeout || 60000;
this.failureCount = 0;
this.lastFailureTime = 0;
this.state = 'closed';
}
async call(fn, ...args) {
if (this.state === 'open') {
if (Date.now() - this.lastFailureTime > this.recoveryTimeout) {
this.state = 'half-open';
console.log('[circuit] State: half-open');
} else {
const remaining = this.recoveryTimeout - (Date.now() - this.lastFailureTime);
throw new Error(`Circuit open — retry in ${Math.ceil(remaining / 1000)}s`);
}
}
try {
const result = await fn(...args);
this.failureCount = 0;
if (this.state === 'half-open') {
console.log('[circuit] State: closed — recovered');
}
this.state = 'closed';
return result;
} catch (error) {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) {
this.state = 'open';
console.log(`[circuit] State: open — ${this.failureCount} failures`);
}
throw error;
}
}
}
// Usage
const axios = require('axios');
const API_KEY = 'YOUR_API_KEY';
const breaker = new CircuitBreaker({ failureThreshold: 3, recoveryTimeout: 30000 });
async function solveCaptcha(sitekey, pageurl) {
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('Timeout');
}
(async () => {
for (let i = 0; i < 10; i++) {
try {
const token = await breaker.call(solveCaptcha, '6Le-SITEKEY', 'https://example.com');
console.log(`[task-${i}] Solved: ${token.substring(0, 40)}...`);
} catch (err) {
console.log(`[task-${i}] ${err.message}`);
}
}
})();
Chọn ngưỡng
| tham số | Lưu lượng truy cập thấp (< 10/min) | Lưu lượng truy cập cao (> 100/min) |
|---|---|---|
failure_threshold |
3 | 10 |
recovery_timeout |
30 giây | 60 giây |
Đặt ngưỡng lỗi đủ cao để chấp nhận các lỗi không liên tục (một lần chờ sẽ không làm ngắt mạch) nhưng đủ thấp để ngăn chặn API bị lỗi.
Kết hợp với logic thử lại
Sử dụng logic thử lại bên trong bộ ngắt mạch. Bộ ngắt mạch đếm các lỗi cuối cùng (sau khi hết số lần thử lại):
def solve_with_retry(sitekey, page_url, max_retries=2):
for attempt in range(max_retries + 1):
try:
return solve_captcha(sitekey, page_url)
except Exception:
if attempt == max_retries:
raise
time.sleep(2 ** attempt)
# Circuit breaker wraps the retry function
token = breaker.call(solve_with_retry, "6Le-SITEKEY", "https://example.com")
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Mạch mở quá nhanh | Ngưỡng quá thấp | Tăng failure_threshold |
| Mạch không bao giờ phục hồi | recovery_timeout quá dài |
Giảm xuống còn 30-60 giây |
| Điều kiện chạy đua trong sử dụng đa luồng | Không có trạng thái khóa | Sử dụng threading.Lock (Python) hoặc các hoạt động nguyên tử |
| Tất cả các yêu cầu bị chặn trong thời gian ngừng hoạt động một phần | Bộ ngắt đơn cho tất cả các điểm cuối | Sử dụng các bộ ngắt riêng biệt cho điểm cuối gửi và thăm dò ý kiến |
Câu hỏi thường gặp
Tôi có nên sử dụng bộ ngắt mạch riêng biệt để gửi và thăm dò ý kiến không?
Có, đối với các hệ thống quy mô lớn. Điểm cuối gửi có thể không thành công trong khi việc bỏ phiếu vẫn hoạt động (hoặc ngược lại). Bộ ngắt riêng biệt giúp bạn kiểm soát chi tiết hơn.
Tôi nên làm gì khi mạch bị hở?
Xếp hàng tác vụ CAPTCHA để thực hiện sau, hiển thị giao diện người dùng dự phòng hoặc bỏ qua thao tác. Nhìn thấySự xuống cấp duyên dáng khi giải quyết thất bại.
Xây dựng quy trình làm việc CAPTCHA linh hoạt với CaptchaAI
Nhận khóa API của bạn tạicaptchaai.com.
Hướng dẫn liên quan
- Triển khai logic thử lại cho API CaptchaAI
- Tham khảo mã lỗi CaptchaAI
- Sự xuống cấp duyên dáng khi giải quyết thất bại