Khắc Phục Sự Cố

Giới hạn yêu cầu đồng thời của CaptchaAI: Chẩn đoán và khắc phục

Bạn tăng số luồng scraping hoặc script QA lên 30–50 tiến trình và CaptchaAI lập tức trả về ERROR_NO_SLOT_AVAILABLE, hoặc worker của bạn dính HTTP 429 dù chưa gửi nhiều task? Đây không phải lỗi ngẫu nhiên — mỗi gói CaptchaAI có một giới hạn số task được giải đồng thời, và giới hạn đó gắn trực tiếp với số thread trong gói bạn đang dùng.

Bài này đi thẳng vào bốn cách khắc phục thực tế: giới hạn concurrency bằng semaphore, retry có backoff, dùng hàng đợi tác vụ, và giãn tần suất polling — kèm cách nhận biết khi nào nên nâng gói thay vì tối ưu thêm code.


Dấu hiệu bạn đang chạm giới hạn đồng thời

  • ERROR_NO_SLOT_AVAILABLE — đang có quá nhiều task chạy cùng lúc.
  • HTTP 429 — gửi quá nhiều request/giây tới endpoint API.
  • Một phần task lỗi, phần khác vẫn chạy tốt — chạm giới hạn không liên tục, tùy thời điểm tải.
  • Thời gian giải tăng dần — hàng đợi trong tài khoản của bạn bị nghẽn.

Hai loại giới hạn CaptchaAI áp dụng

  • Số task đồng thời — số CAPTCHA tối đa đang được giải cùng lúc; vượt quá sẽ trả ERROR_NO_SLOT_AVAILABLE.
  • Tần suất request — số lệnh gọi API tối đa mỗi giây tới endpoint gửi/poll; vượt quá sẽ trả HTTP 429.

Giới hạn số task đồng thời gắn với số thread của gói bạn đăng ký. BASIC ($15/tháng, 5 thread) đủ cho script cá nhân; STANDARD ($30/tháng, 15 thread) hoặc ADVANCE ($90/tháng, 50 thread) hợp với pipeline tầm trung; đội automation cần nhiều job song song thường cần CORPORATE ($240/tháng, 150 thread) trở lên.

Mẹo: đặt MAX_CONCURRENT thấp hơn giới hạn gói khoảng 20% khi mới triển khai, rồi tăng dần sau khi xác nhận throughput ổn định. Kiểm tra dashboard tại captchaai.com để biết giới hạn hiện tại của tài khoản bạn.


Ví dụ thực tế: đội QA theo dõi giá trên sàn thương mại điện tử

Một đội automation ở công ty outsourcing tại TP.HCM chạy script theo dõi giá trên Shopee và Tiki mỗi đêm bằng Selenium, dùng nhiều phiên song song để rút ngắn thời gian crawl.

Khi họ tăng từ 20 lên 50 luồng mà tài khoản vẫn ở gói STANDARD (15 thread), phần lớn task bắt đầu trả ERROR_NO_SLOT_AVAILABLE — không phải vì code sai, mà vì số luồng đã vượt quá thread được cấp. Cách xử lý đúng là dùng semaphore giới hạn concurrency ở mức tài khoản cho phép, đồng thời cân nhắc nâng gói nếu khối lượng job tăng đều theo thời gian.


Cách 1: Giới hạn concurrency bằng semaphore

Kiểm soát chặt số task chạy song song ngay trong code, thay vì để tất cả request bắn đi cùng lúc:

import requests
import time
import threading

API_KEY = "YOUR_API_KEY"
MAX_CONCURRENT = 20  # Stay below your account limit

semaphore = threading.Semaphore(MAX_CONCURRENT)

def solve_captcha(params):
    """Solve a CAPTCHA with concurrency control."""
    with semaphore:
        params["key"] = API_KEY
        params["json"] = 1

        submit = requests.post("https://ocr.captchaai.com/in.php", data=params).json()
        if submit.get("status") != 1:
            raise RuntimeError(f"Submit: {submit.get('request')}")

        task_id = submit["request"]
        time.sleep(10)

        for _ in range(30):
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": API_KEY, "action": "get", "id": task_id, "json": 1
            }).json()
            if result.get("status") == 1:
                return result["request"]
            if result.get("request") != "CAPCHA_NOT_READY":
                raise RuntimeError(f"Solve: {result['request']}")
            time.sleep(5)
        raise TimeoutError("Timed out")

Cách 2: Retry tự động khi gặp ERROR_NO_SLOT_AVAILABLE

Nguyên tắc: khi chạm giới hạn, đợi rồi thử lại theo backoff tăng dần — đừng để một lần ERROR_NO_SLOT_AVAILABLE làm chết cả script.

def submit_with_retry(params, max_retries=5):
    """Submit with automatic retry for slot errors."""
    params["key"] = API_KEY
    params["json"] = 1

    for attempt in range(max_retries):
        resp = requests.post("https://ocr.captchaai.com/in.php", data=params).json()

        if resp.get("status") == 1:
            return resp["request"]

        error = resp.get("request", "")
        if error == "ERROR_NO_SLOT_AVAILABLE":
            wait = 2 ** attempt  # Exponential backoff: 1, 2, 4, 8, 16 seconds
            print(f"No slot available, retrying in {wait}s (attempt {attempt + 1})")
            time.sleep(wait)
            continue
        else:
            raise RuntimeError(f"Submit error: {error}")

    raise RuntimeError("Max retries exceeded — no slots available")

Cách 3: Dùng hàng đợi tác vụ thay vì gửi ồ ạt

Thay vì đẩy toàn bộ task vào API cùng lúc, xếp hàng và để một nhóm worker cố định xử lý theo tốc độ được kiểm soát.

from queue import Queue
from threading import Thread

task_queue = Queue()
results = {}

def worker():
    while True:
        task_id_local, params = task_queue.get()
        try:
            token = solve_captcha(params)
            results[task_id_local] = {"status": "ok", "token": token}
        except Exception as e:
            results[task_id_local] = {"status": "error", "message": str(e)}
        finally:
            task_queue.task_done()

# Start worker threads (limited by semaphore)
for _ in range(MAX_CONCURRENT):
    t = Thread(target=worker, daemon=True)
    t.start()

# Add tasks to queue
captcha_tasks = [
    {"method": "userrecaptcha", "googlekey": "KEY1", "pageurl": "https://site1.com"},
    {"method": "userrecaptcha", "googlekey": "KEY2", "pageurl": "https://site2.com"},
    # ... more tasks
]

for i, params in enumerate(captcha_tasks):
    task_queue.put((i, params))

task_queue.join()
print(f"Completed: {len(results)} tasks")

Cách 4: Giãn tần suất polling

Poll quá dày vừa tốn lệnh gọi API vừa dễ kích hoạt giới hạn tần suất request. Hai lỗi thường gặp:

  • Poll mỗi 1 giây ngay từ đầu — tốn API call mà chưa chắc task đã giải xong.
  • Không có initial delay trước lần poll đầu tiên, dẫn đến hàng loạt request rỗng.
# WRONG — polling every 1 second
time.sleep(1)

# CORRECT — poll every 5 seconds
time.sleep(5)

# BETTER — wait longer on initial delay, then poll
time.sleep(15)  # Initial wait
for _ in range(20):
    # ... poll
    time.sleep(5)

Theo dõi số task đang chạy theo thời gian thực

  • Ghi log số task hiện tại mỗi khi một task bắt đầu và kết thúc.
  • Dùng cùng với semaphore ở Cách 1 để biết bạn còn cách giới hạn bao xa trước khi lỗi xảy ra.
active_count = 0
lock = threading.Lock()

def track_solve(params):
    global active_count
    with lock:
        active_count += 1
        print(f"Active tasks: {active_count}/{MAX_CONCURRENT}")
    try:
        return solve_captcha(params)
    finally:
        with lock:
            active_count -= 1

Khi nào nên nâng gói thay vì tối ưu thêm code

  • Nâng gói khi đã áp dụng semaphore, retry và hàng đợi mà vẫn thường xuyên chạm ERROR_NO_SLOT_AVAILABLE ở mức tải bình thường. Đó là dấu hiệu khối lượng công việc đã vượt thread của gói hiện tại, không phải lỗi code; nâng từ STANDARD lên ADVANCE hoặc CORPORATE thường rẻ hơn thời gian kỹ sư vắt thêm hiệu năng từ một giới hạn cứng.
  • Tối ưu code là đủ khi lỗi chỉ xuất hiện theo đợt — ví dụ batch job vào khung giờ cố định mỗi đêm. Semaphore kết hợp hàng đợi thường giải quyết được mà không cần nâng gói.

Câu hỏi thường gặp

Cần bao nhiêu thread để scrape khoảng 10.000 trang mỗi ngày?

Không có con số cố định — phụ thuộc vào thời gian giải trung bình của loại CAPTCHA và độ trễ giữa các request. Bắt đầu với MAX_CONCURRENT thấp hơn giới hạn gói, đo throughput thực tế, rồi nâng gói nếu chưa đủ.

Polling có tính vào giới hạn tần suất request không?

Có. Mỗi lệnh gọi res.php đều tính vào giới hạn request/giây, kể cả khi bạn chỉ đang hỏi kết quả. Poll mỗi 5 giây, không phải mỗi 1 giây.

Polling hay webhook giúp tránh HTTP 429 tốt hơn?

Webhook (callback) giảm số request vì CaptchaAI tự gửi kết quả về URL của bạn thay vì bạn phải hỏi liên tục qua res.php. Nếu hệ thống nhận được callback công khai, đây là cách hiệu quả hơn polling dày đặc.

Có thể tăng giới hạn đồng thời không?

Có — liên hệ hỗ trợ CaptchaAI hoặc nâng gói để tăng số thread.

Semaphore có làm chậm tốc độ giải không?

Không đáng kể nếu MAX_CONCURRENT gần với giới hạn thực tế của gói — nó chỉ chặn task vượt ngưỡng chờ đến lượt, ngăn CaptchaAI trả lỗi thay vì để bạn phải retry hàng loạt.


Hướng dẫn liên quan

Các bài sau đi sâu hơn vào từng phần của giới hạn tần suất và xử lý hàng đợi:


Kiểm tra giới hạn thread trong dashboard CaptchaAI và nâng gói khi cần

Xem giới hạn đồng thời hiện tại, đối chiếu với khối lượng task bạn đang chạy, và nâng gói trực tiếp tại captchaai.com nếu cần thêm thread. Phần lớn trường hợp ERROR_NO_SLOT_AVAILABLE xử lý được bằng code trước khi cần trả thêm tiền cho gói cao hơn.

Os comentários estão desativados para este artigo.