عندما تكون واجهة برمجة التطبيقات لحل اختبار CAPTCHA بطيئة أو تعرض أخطاء، فإن الاستمرار في إرسال الطلبات يضيع الوقت والمال. يتوقف نمط قاطع الدائرة عن استدعاء واجهة برمجة التطبيقات الفاشلة، وينتظر الاسترداد، ويستأنف العمل تلقائيًا - مما يمنع حالات الفشل المتتالية في خط الأنابيب الخاص بك.
كيف تعمل قواطع الدائرة
ثلاث ولايات:
- مغلق — تشغيل عادي. تمر الطلبات. يتم احتساب الفشل.
- مفتوح — هناك عدد كبير جدًا من حالات الفشل. يتم رفض جميع الطلبات على الفور دون استدعاء واجهة برمجة التطبيقات.
- نصف مفتوح — بعد فترة تهدئة، يُسمح بطلب اختبار واحد. إذا نجحت، يتم إغلاق الدائرة. إذا فشلت، يتم فتح الدائرة مرة أخرى.
تنفيذ بايثون
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}")
الناتج المتوقع:
[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
تنفيذ 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}`);
}
}
})();
اختيار العتبات
| المعلمة | حركة مرور منخفضة (<10/min) | حركة مرور عالية (> 100/min) |
|---|---|---|
failure_threshold |
3 | 10 |
recovery_timeout |
30 ثانية | الستينيات |
قم بتعيين حد الفشل مرتفعًا بما يكفي لتحمل الأخطاء المتقطعة (لا ينبغي أن تؤدي مهلة واحدة إلى تعطيل الدائرة) ولكن منخفضة بما يكفي لإيقاف ضرب واجهة برمجة التطبيقات الفاشلة.
الجمع مع منطق إعادة المحاولة
استخدم منطق إعادة المحاولة داخل قاطع الدائرة. يحسب قاطع الدائرة حالات الفشل النهائية (بعد استنفاد عمليات إعادة المحاولة):
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")
استكشاف الأخطاء وإصلاحها
| مشكلة | السبب | إصلاح |
|---|---|---|
| تفتح الدائرة بسرعة كبيرة | العتبة منخفضة جدًا | زيادة failure_threshold |
| الدائرة لا تتعافى أبدًا | recovery_timeout طويل جدًا |
تقليل إلى 30-60 ثانية |
| حالة السباق في الاستخدام متعدد Threads | لا يوجد قفل على الدولة | استخدم threading.Lock (Python) أو العمليات الذرية |
| تم حظر جميع الطلبات أثناء الانقطاع الجزئي | قاطع واحد لجميع نقاط النهاية | استخدم قواطع منفصلة لنقاط نهاية الإرسال والاستقصاء |
الأسئلة الشائعة
هل يجب علي استخدام قواطع دوائر منفصلة للإرسال والاستطلاع؟
نعم، للأنظمة واسعة النطاق. قد تفشل نقطة نهاية الإرسال بينما لا يزال الاستقصاء يعمل (أو العكس). تمنحك القواطع المنفصلة تحكمًا أكثر دقة.
ماذا يجب أن أفعل عندما تكون الدائرة مفتوحة؟
قم بوضع مهمة CAPTCHA في قائمة الانتظار لوقت لاحق، أو قم بإظهار واجهة مستخدم احتياطية، أو تخطي العملية. يرىالتدهور اللطيف عند فشل الحل.
قم ببناء سير عمل CAPTCHA مرن باستخدام CaptchaAI
احصل على مفتاح API الخاص بك علىcaptchaai.com.