Đồng thời không được kiểm soát sẽ gửi yêu cầu nhanh nhất có thể. Điều đó dẫn đến ERROR_TOO_MUCH_REQUESTS, lãng phí số dư API và chi phí không thể đoán trước. Nhóm mã thông báo cho phép bạn đặt tốc độ chính xác - "không quá 20 lần gửi mỗi giây" - trong khi vẫn cho phép các đợt ngắn khi có đủ dung lượng.
Cách thức hoạt động của nhóm mã thông báo
[Bucket] capacity=20, refill=10/sec
Time 0: ████████████████████ 20 tokens available
→ 15 requests consume 15 tokens
Time 0: █████ 5 tokens remain
Time 1s: ███████████████ 15 tokens (5 + 10 refilled)
→ 15 requests consume 15 tokens
Time 1s: (empty) 0 tokens
Time 2s: ██████████ 10 tokens (0 + 10 refilled)
→ Request waits if bucket is empty
Thuộc tính chính:
- Dung lượng — kích thước cụm tối đa
- Tốc độ nạp lại — số yêu cầu được duy trì mỗi giây
- Yêu cầu chờ khi thùng trống (không từ chối, chỉ điều chỉnh)
Triển khai Python
Nhóm mã thông báo an toàn theo chủ đề
import time
import threading
class TokenBucket:
def __init__(self, capacity, refill_rate):
"""
Args:
capacity: Maximum tokens (burst size)
refill_rate: Tokens added per second
"""
self.capacity = capacity
self.refill_rate = refill_rate
self.tokens = capacity
self.last_refill = time.monotonic()
self.lock = threading.Lock()
def acquire(self, timeout=None):
"""Block until a token is available."""
deadline = time.monotonic() + timeout if timeout else float("inf")
while True:
with self.lock:
self._refill()
if self.tokens >= 1:
self.tokens -= 1
return True
# Check timeout
if time.monotonic() >= deadline:
return False
# Wait before retrying (avoid busy loop)
time.sleep(min(1.0 / self.refill_rate, 0.1))
def _refill(self):
now = time.monotonic()
elapsed = now - self.last_refill
new_tokens = elapsed * self.refill_rate
self.tokens = min(self.capacity, self.tokens + new_tokens)
self.last_refill = now
Bộ giải CAPTCHA có giới hạn tỷ lệ
import os
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
# Allow 10 submissions/sec with burst of 20
rate_limiter = TokenBucket(capacity=20, refill_rate=10)
def solve_captcha_rate_limited(sitekey, pageurl):
"""Solve with rate limiting on submission."""
# Wait for token before submitting
rate_limiter.acquire()
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1
})
data = resp.json()
if data.get("status") != 1:
raise RuntimeError(data.get("request"))
captcha_id = data["request"]
# Polling doesn't need rate limiting (separate concern)
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": captcha_id, "json": 1
}).json()
if result.get("status") == 1:
return result["request"]
if result.get("request") != "CAPCHA_NOT_READY":
raise RuntimeError(result.get("request"))
raise TimeoutError("Solve timeout")
# Run 100 tasks through rate limiter
tasks = [
{"sitekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
"pageurl": f"https://example.com/p/{i}"}
for i in range(100)
]
with ThreadPoolExecutor(max_workers=30) as executor:
futures = {
executor.submit(
solve_captcha_rate_limited, t["sitekey"], t["pageurl"]
): t for t in tasks
}
for future in as_completed(futures):
task = futures[future]
try:
solution = future.result()
print(f"[OK] {task['pageurl']}")
except Exception as e:
print(f"[ERR] {task['pageurl']}: {e}")
Triển khai JavaScript
Nhóm mã thông báo không đồng bộ
class TokenBucket {
constructor(capacity, refillRate) {
this.capacity = capacity;
this.refillRate = refillRate; // tokens per second
this.tokens = capacity;
this.lastRefill = Date.now();
this.waitQueue = [];
}
_refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
async acquire() {
this._refill();
if (this.tokens >= 1) {
this.tokens -= 1;
return;
}
// Wait until a token is available
const waitTime = ((1 - this.tokens) / this.refillRate) * 1000;
await new Promise((resolve) => setTimeout(resolve, waitTime));
this._refill();
this.tokens -= 1;
}
}
Bộ giải hàng loạt có giới hạn tỷ lệ
const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const rateLimiter = new TokenBucket(20, 10); // 20 burst, 10/sec sustained
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveCaptchaLimited(sitekey, pageurl) {
// Wait for rate limit token
await rateLimiter.acquire();
const submitResp = await axios.post(
"https://ocr.captchaai.com/in.php",
null,
{
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageurl,
json: 1,
},
}
);
if (submitResp.data.status !== 1) {
throw new Error(submitResp.data.request);
}
const captchaId = submitResp.data.request;
for (let i = 0; i < 60; i++) {
await sleep(5000);
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
});
if (result.data.status === 1) return result.data.request;
if (result.data.request !== "CAPCHA_NOT_READY") {
throw new Error(result.data.request);
}
}
throw new Error("TIMEOUT");
}
// Solve 100 tasks — rate limiter ensures max 10 submissions/sec
async function batchSolve(tasks) {
const results = await Promise.allSettled(
tasks.map((t) => solveCaptchaLimited(t.sitekey, t.pageurl))
);
const solved = results.filter((r) => r.status === "fulfilled").length;
const failed = results.filter((r) => r.status === "rejected").length;
console.log(`Solved: ${solved}, Failed: ${failed}`);
}
Chọn tham số
| Khối lượng công việc | Công suất (nổ) | Tỷ lệ nạp lại (duy trì) |
|---|---|---|
| Cạo nhẹ | 5 | 2/sec |
| Tự động hóa tiêu chuẩn | 20 | 10/sec |
| Đường ống khối lượng lớn | 50 | 30/sec |
| Thông lượng tối đa | 100 | 50/sec |
Quy tắc chung:
- Đặt công suất thành 2× tốc độ nạp lại (cho phép bùng nổ trong 2 giây)
- Bắt đầu thận trọng, tăng lên trong khi theo dõi tỷ lệ lỗi
- Chỉ gửi giới hạn tỷ lệ - bỏ phiếu rất nhẹ và tự giới hạn
Nhóm mã thông báo so với các thuật toán khác
| Thuật toán | Hành vi | Tốt nhất cho |
|---|---|---|
| Nhóm mã thông báo | Tốc độ mượt mà với phụ cấp bùng nổ | Lệnh gọi API CAPTCHA |
| Xô bị rò rỉ | Tốc độ đầu ra cố định, không nổ | Yêu cầu tỷ lệ nghiêm ngặt |
| Cửa sổ cố định | Đếm trên mỗi cửa sổ thời gian, bùng nổ cạnh | quầy đơn giản |
| Cửa sổ trượt | Đếm trong thời gian lăn | Thực thi tỷ giá chính xác |
Nhóm mã thông báo là mặc định tốt nhất — nó cho phép bùng nổ tự nhiên (trình quét tìm thấy 20 CAPTCHA cùng một lúc) trong khi thực thi tốc độ duy trì.
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Yêu cầu vẫn đang bị hạn chế | Bộ giới hạn tốc độ được đặt cao hơn mức API cho phép | Tỷ lệ nạp lại thấp hơn để phù hợp với giới hạn của CaptchaAI |
| Độ trễ cao theo yêu cầu | Token đã cạn, đang chờ nạp lại | Tăng công suất cho các kịch bản bùng nổ |
| Trí nhớ ngày càng tăng | Hàng chờ tích lũy | Đặt kích thước hàng đợi tối đa; từ chối yêu cầu vượt quá |
| Giới hạn tốc độ không được chia sẻ giữa các quy trình | Chỉ trong bộ nhớ | Sử dụng nhóm mã thông báo dựa trên Redis để giới hạn tốc độ phân phối |
Câu hỏi thường gặp
Tôi nên giới hạn tỷ lệ gửi bài, bỏ phiếu hay cả hai?
Chỉ gửi giới hạn tỷ lệ. Yêu cầu thăm dò rất nhẹ và tự điều chỉnh thông qua time.sleep(5). Bỏ phiếu quá giới hạn làm tăng giải quyết độ trễ mà không có lợi.
Làm cách nào để xử lý ERROR_TOO_MUCH_REQUESTS mặc dù bị giới hạn tốc độ?
Giới hạn tỷ lệ của bạn được đặt quá cao. Giảm tỷ lệ nạp lại. Đồng thời kiểm tra xem nhiều quy trình có chia sẻ cùng một khóa API hay không - tỷ lệ tổng hợp trên tất cả các quy trình.
Tôi có thể sử dụng bộ giới hạn tốc độ cho mỗi loại CAPTCHA không?
Có - tạo các nhóm mã thông báo riêng biệt cho các loại CAPTCHA khác nhau. Điều này ngăn không cho các tác vụ reCAPTCHA v2 có khối lượng lớn bỏ đói các lần gửi Turnstile.
bài viết liên quan
Các bước tiếp theo
Xây dựng giải pháp CAPTCHA được kiểm soát theo tỷ lệ —lấy khóa API CaptchaAI của bạnvà thực hiện tỷ lệ yêu cầu bền vững.
Hướng dẫn liên quan: