Khi cơ sở hạ tầng quét của bạn gửi hàng nghìn yêu cầu giải quyết CAPTCHA, quy trình của một nhân viên sẽ bị tắc nghẽn. Bộ cân bằng tải phân phối yêu cầu trên nhiều trình chạy — cải thiện thông lượng, cho phép chuyển đổi dự phòng và cho phép bạn mở rộng quy mô theo chiều ngang.
Tổng quan về kiến trúc
[Scraper 1] ──┐ ┌── [Worker 1] ──→ CaptchaAI API
[Scraper 2] ──┤── [Load Balancer] ──┤── [Worker 2] ──→ CaptchaAI API
[Scraper 3] ──┘ └── [Worker 3] ──→ CaptchaAI API
Cấu hình NGINX
Vòng tròn (Mặc định)
upstream captcha_workers {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
listen 80;
server_name captcha.internal;
location /solve {
proxy_pass http://captcha_workers;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 10s;
proxy_read_timeout 300s; # CAPTCHA solving can take minutes
}
location /health {
proxy_pass http://captcha_workers;
proxy_connect_timeout 5s;
proxy_read_timeout 5s;
}
}
Ít kết nối nhất (Tốt hơn cho việc giải CAPTCHA)
upstream captcha_workers {
least_conn; # Route to worker with fewest active connections
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080 weight=2; # Higher capacity worker
# Health checks
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.12:8080 max_fails=3 fail_timeout=30s;
}
Với công nhân dự phòng
upstream captcha_workers {
least_conn;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080 backup; # Only used when others are down
}
Máy chủ API công nhân
Python (Bình)
import os
import time
import threading
import requests
from flask import Flask, request, jsonify
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
app = Flask(__name__)
# Track active tasks for load reporting
active_tasks = 0
tasks_lock = threading.Lock()
max_concurrent = int(os.environ.get("MAX_CONCURRENT", "20"))
@app.route("/solve", methods=["POST"])
def solve():
global active_tasks
with tasks_lock:
if active_tasks >= max_concurrent:
return jsonify({"error": "WORKER_AT_CAPACITY"}), 503
active_tasks += 1
try:
data = request.json
result = solve_captcha(data)
return jsonify(result)
finally:
with tasks_lock:
active_tasks -= 1
@app.route("/health")
def health():
with tasks_lock:
load = active_tasks / max_concurrent
return jsonify({
"status": "healthy" if load < 0.9 else "overloaded",
"active_tasks": active_tasks,
"max_concurrent": max_concurrent,
"load_pct": round(load * 100, 1)
}), 200 if load < 0.9 else 503
def solve_captcha(data):
session = requests.Session()
payload = {
"key": API_KEY,
"method": data.get("method", "userrecaptcha"),
"googlekey": data.get("sitekey"),
"pageurl": data.get("pageurl"),
"json": 1
}
if data.get("proxy"):
payload["proxy"] = data["proxy"]
payload["proxytype"] = data.get("proxytype", "HTTP")
resp = session.post("https://ocr.captchaai.com/in.php", data=payload)
result = resp.json()
if result.get("status") != 1:
return {"error": result.get("request")}
captcha_id = result["request"]
for _ in range(60):
time.sleep(5)
poll = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": captcha_id, "json": 1
}).json()
if poll.get("status") == 1:
return {"solution": poll["request"], "captcha_id": captcha_id}
if poll.get("request") != "CAPCHA_NOT_READY":
return {"error": poll.get("request")}
return {"error": "TIMEOUT"}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080, threaded=True)
JavaScript (Express)
const express = require("express");
const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const MAX_CONCURRENT = parseInt(process.env.MAX_CONCURRENT || "20", 10);
const PORT = parseInt(process.env.PORT || "8080", 10);
let activeTasks = 0;
const app = express();
app.use(express.json());
app.post("/solve", async (req, res) => {
if (activeTasks >= MAX_CONCURRENT) {
return res.status(503).json({ error: "WORKER_AT_CAPACITY" });
}
activeTasks++;
try {
const result = await solveCaptcha(req.body);
res.json(result);
} catch (err) {
res.status(500).json({ error: err.message });
} finally {
activeTasks--;
}
});
app.get("/health", (req, res) => {
const load = activeTasks / MAX_CONCURRENT;
const status = load < 0.9 ? "healthy" : "overloaded";
res
.status(load < 0.9 ? 200 : 503)
.json({ status, activeTasks, maxConcurrent: MAX_CONCURRENT, loadPct: Math.round(load * 100) });
});
async function solveCaptcha(data) {
const submitResp = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: API_KEY,
method: data.method || "userrecaptcha",
googlekey: data.sitekey,
pageurl: data.pageurl,
json: 1,
},
});
if (submitResp.data.status !== 1) {
return { error: submitResp.data.request };
}
const captchaId = submitResp.data.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const pollResp = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
});
if (pollResp.data.status === 1) {
return { solution: pollResp.data.request, captchaId };
}
if (pollResp.data.request !== "CAPCHA_NOT_READY") {
return { error: pollResp.data.request };
}
}
return { error: "TIMEOUT" };
}
app.listen(PORT, () => console.log(`Worker listening on port ${PORT}`));
So sánh các chiến lược định tuyến
| Chiến lược | Nó hoạt động như thế nào | Tốt nhất cho |
|---|---|---|
| Vòng tròn | Xoay tuần tự | Người lao động có năng lực ngang nhau |
| Ít kết nối nhất | Tuyến đường đến nơi ít tải nhất | Giải CAPTCHA (thời lượng tác vụ có thể thay đổi) |
| Có trọng số | Tỷ lệ thuận với trọng lượng | Công nhân có năng lực hỗn hợp |
| Băm IP | Cùng một khách hàng – cùng một nhân viên | Cần có mối quan hệ phiên |
| Ngẫu nhiên | Lựa chọn ngẫu nhiên | Tải trọng phân bố đều, đơn giản |
Khuyến nghị: Sử dụng ít kết nối nhất để giải CAPTCHA. Thời lượng nhiệm vụ khác nhau (5 giây–120 giây), do đó, việc quay vòng sẽ tạo ra tải không đồng đều.
Cân bằng tải phía máy khách
Khi bạn không thể sử dụng bộ cân bằng tải bên ngoài, hãy triển khai định tuyến trong máy khách:
import random
import requests
class ClientLoadBalancer:
def __init__(self, workers):
self.workers = [
{"url": url, "healthy": True, "active": 0}
for url in workers
]
def get_worker(self):
healthy = [w for w in self.workers if w["healthy"]]
if not healthy:
raise Exception("No healthy workers")
return min(healthy, key=lambda w: w["active"])
def solve(self, task):
worker = self.get_worker()
worker["active"] += 1
try:
resp = requests.post(
f"{worker['url']}/solve",
json=task,
timeout=300
)
if resp.status_code == 503:
worker["healthy"] = False
return self.solve(task) # Retry on another worker
return resp.json()
except requests.RequestException:
worker["healthy"] = False
return self.solve(task)
finally:
worker["active"] -= 1
lb = ClientLoadBalancer([
"http://10.0.1.10:8080",
"http://10.0.1.11:8080",
"http://10.0.1.12:8080"
])
result = lb.solve({"sitekey": "6Le-wvkS...", "pageurl": "https://example.com"})
Ma trận lựa chọn thuật toán
- Sử dụng luân chuyển khi các nhân viên đồng nhất và thời gian giải quyết nằm trong dải độ trễ hẹp.
- Sử dụng ít kết nối nhất khi thời lượng giải quyết thay đổi và các thử thách kéo dài sẽ chồng chất lên một công nhân.
- Chỉ giữ định tuyến dự phòng và mối quan hệ nguồn cho các mục tiêu cách ly lỗi hoặc nhạy cảm với phiên.
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| 502 Cổng xấu | Công nhân gặp sự cố hoặc chưa khởi động | Kiểm tra nhật ký công nhân; xác minh ràng buộc cổng |
| Phân phối tải không đồng đều | Xoay vòng với thời lượng nhiệm vụ thay đổi | Chuyển sang ít kết nối nhất |
| Kiểm tra sức khỏe dương tính giả | Kiểm tra thẻ nhưng công nhân đang làm việc hết công suất | Bao gồm phần trăm tải trong phản hồi sức khỏe |
| Hết thời gian kết nối | proxy_read_timeout quá ngắn |
Đặt thành 300+ để giải CAPTCHA |
Câu hỏi thường gặp
Tôi có cần cân bằng tải cho 2-3 công nhân không?
Cân bằng tải phía máy khách hoạt động tốt đối với các thiết lập nhỏ. Sử dụng bộ cân bằng tải chuyên dụng (NGINX, HAProxy) khi bạn có hơn 5 nhân viên hoặc cần các tính năng như chấm dứt SSL và kiểm tra tình trạng.
Tôi có nên sử dụng phiên dính?
Không. Yêu cầu giải quyết CAPTCHA không có trạng thái — bất kỳ nhân viên nào cũng có thể xử lý bất kỳ tác vụ nào. Các phiên cố định sẽ tạo ra sự phân bổ tải không đồng đều.
Làm cách nào để xử lý công nhân ở các khu vực khác nhau?
Sử dụng bộ cân bằng tải toàn cầu (AWS Global Accelerator, Cloudflare Load Balancing) để định tuyến đến khu vực hoạt động tốt gần nhất. Mỗi khu vực chạy bộ cân bằng tải cục bộ riêng cho nhân viên trong khu vực đó.
bài viết liên quan
- Các mẫu xử lý lỗi gọi lại Captchaai
- Các mẫu nâng cao của Nodejs Puppeteer Captchaai
- Giải quyết các mẫu kiến trúc Captcha Khối lượng lớn
Các bước tiếp theo
Tăng quy mô thông lượng giải CAPTCHA của bạn —lấy khóa API CaptchaAI của bạnvà triển khai đằng sau bộ cân bằng tải.
Hướng dẫn liên quan:
- Chuyển đổi dự phòng có tính sẵn sàng cao
- Kiến trúc đa vùng
- Kích thước nhóm kết nối