Một region duy nhất là một điểm chết duy nhất: vùng đó rớt mạng, mọi task CAPTCHA của bạn rớt theo; target site ở xa nửa vòng trái đất, mỗi request cõng thêm 100–300 ms round-trip. Kiến trúc đa vùng giải quyết cả hai vấn đề bằng cùng một cách tiếp cận — đặt worker CaptchaAI gần target site để giảm độ trễ, và tự động chuyển tải sang vùng khác khi một vùng gặp sự cố.
Nhiều đội outsourcing ở TP.HCM chạy QA cho khách Mỹ, châu Âu theo đúng lộ trình này: một worker ở us-east-1, rồi thêm eu-west-1 khi độ trễ ăn vào SLA.
Sơ đồ tổng quan
Mô hình gồm ba lớp: task router phân phối request theo vùng gần target site nhất, fleet worker độc lập từng vùng, và một API key dùng chung.
[Task Router]
(Route53 / Load Balancer)
↙ ↓ ↘
[US-East] [EU-West] [AP-Southeast]
Workers Workers Workers
↓ ↓ ↓
[CaptchaAI API] ← shared API key
↓ ↓ ↓
[Central DB / Queue]
(Results aggregation)
Mỗi vùng chạy worker riêng, không phụ thuộc nhau, cùng đẩy kết quả về một hàng đợi trung tâm.
Khi nào nên triển khai đa vùng?
Đa vùng không phải lựa chọn mặc định — nó giải quyết một vấn đề cụ thể. Đối chiếu với tình huống của bạn trước khi thêm hạ tầng:
| Tình huống | Một vùng | Đa vùng |
|---|---|---|
| Target site chỉ ở một quốc gia | Đủ dùng | Thừa, tốn công vận hành |
| Target site trải nhiều châu lục | Cộng thêm 100–300 ms round-trip | Độ trễ cục bộ |
| Yêu cầu uptime 99,9% | Khó đảm bảo, một điểm lỗi | Dư thừa tự nhiên |
| Dữ liệu phải lưu theo khu vực (residency) | Không đáp ứng được | Xử lý, lưu cục bộ |
| < 1.000 task/giờ | Ổn, không cần phức tạp | Không đáng đầu tư thêm |
| > 10.000 task/giờ | Dễ chạm rate limit | Phân tải, giảm nghẽn |
Dòng "residency" liên quan Nghị định 13/2023/NĐ-CP.
Triển khai worker theo từng vùng
Worker Python nhận diện vùng
Mỗi worker đọc region từ biến môi trường, gửi task tới CaptchaAI, rồi gắn nhãn region vào kết quả.
import os
import time
import requests
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
REGION = os.environ.get("WORKER_REGION", "us-east-1")
RESULT_QUEUE_URL = os.environ["RESULT_QUEUE_URL"]
def solve_captcha(task):
"""Solve CAPTCHA and tag with region metadata."""
start = time.time()
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": task["method"],
"googlekey": task["sitekey"],
"pageurl": task["pageurl"],
"json": 1
})
data = resp.json()
if data.get("status") != 1:
return {
"task_id": task["task_id"],
"error": data.get("request"),
"region": REGION
}
captcha_id = data["request"]
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 {
"task_id": task["task_id"],
"solution": result["request"],
"region": REGION,
"duration": time.time() - start,
"api_latency_ms": round((time.time() - start) * 1000)
}
if result.get("request") != "CAPCHA_NOT_READY":
return {
"task_id": task["task_id"],
"error": result.get("request"),
"region": REGION
}
return {"task_id": task["task_id"], "error": "TIMEOUT", "region": REGION}
solve_captcha() luôn trả về region, kể cả khi lỗi hoặc timeout, nên downstream biết ngay task chạy ở đâu.
Định tuyến task tới vùng gần nhất
Router chọn vùng theo domain của pageurl, không theo vị trí worker gọi request:
from urllib.parse import urlparse
# Region mapping by target site TLD/domain
REGION_MAP = {
".co.uk": "eu-west-1",
".de": "eu-central-1",
".fr": "eu-west-3",
".jp": "ap-northeast-1",
".com.au": "ap-southeast-2",
".com": "us-east-1", # Default
}
REGION_QUEUES = {
"us-east-1": "sqs://captcha-tasks-us-east",
"eu-west-1": "sqs://captcha-tasks-eu-west",
"ap-southeast-1": "sqs://captcha-tasks-ap-southeast",
}
def route_task(task):
"""Route task to the closest regional queue."""
domain = urlparse(task["pageurl"]).netloc
target_region = "us-east-1" # Default
for suffix, region in REGION_MAP.items():
if domain.endswith(suffix):
target_region = region
break
queue = REGION_QUEUES.get(target_region, REGION_QUEUES["us-east-1"])
send_to_queue(queue, task)
return target_region
Chỉ cần liệt kê các TLD bạn thực sự nhắm tới trong REGION_MAP — không cần phủ hết mọi quốc gia.
Chuẩn bị hạ tầng cho từng vùng
Terraform cho fleet worker
Mỗi vùng trong var.regions sinh ra một fleet worker, một hàng đợi SQS riêng, cùng trỏ về một secret API key:
# Define regions
variable "regions" {
default = ["us-east-1", "eu-west-1", "ap-southeast-1"]
}
# Deploy worker fleet per region
module "captcha_workers" {
for_each = toset(var.regions)
source = "./modules/captcha-worker"
region = each.key
worker_count = var.workers_per_region
api_key_secret_arn = aws_secretsmanager_secret.captchaai_key.arn
task_queue_arn = aws_sqs_queue.tasks[each.key].arn
result_queue_arn = aws_sqs_queue.results.arn
}
# SQS queue per region for task intake
resource "aws_sqs_queue" "tasks" {
for_each = toset(var.regions)
name = "captcha-tasks-${each.key}"
}
# Central result queue
resource "aws_sqs_queue" "results" {
name = "captcha-results-central"
}
Docker Compose — mô phỏng đa vùng cục bộ
Mô phỏng ba worker vùng bằng Docker Compose và Redis local trước khi lên Terraform thật:
version: "3.8"
services:
worker-us:
build: ./worker
environment:
- CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
- WORKER_REGION=us-east-1
- TASK_QUEUE=redis://redis:6379/0
depends_on:
- redis
worker-eu:
build: ./worker
environment:
- CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
- WORKER_REGION=eu-west-1
- TASK_QUEUE=redis://redis:6379/1
worker-ap:
build: ./worker
environment:
- CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
- WORKER_REGION=ap-southeast-1
- TASK_QUEUE=redis://redis:6379/2
redis:
image: redis:7-alpine
Giám sát sức khỏe từng vùng
Health check chạy tách biệt khỏi logic giải CAPTCHA, để failover không phụ thuộc worker đang bận:
const axios = require("axios");
const REGIONS = ["us-east-1", "eu-west-1", "ap-southeast-1"];
async function checkRegionHealth() {
const health = {};
for (const region of REGIONS) {
const endpoint = `https://${region}.workers.example.com/health`;
try {
const start = Date.now();
const resp = await axios.get(endpoint, { timeout: 5000 });
health[region] = {
status: "healthy",
latencyMs: Date.now() - start,
activeWorkers: resp.data.activeWorkers,
queueDepth: resp.data.queueDepth,
};
} catch (err) {
health[region] = { status: "unhealthy", error: err.message };
}
}
return health;
}
// Periodic health check
setInterval(async () => {
const health = await checkRegionHealth();
console.table(health);
}, 60000);
60 giây mỗi lần kiểm tra là đủ cho hầu hết khối lượng task.
Xử lý failover khi một vùng gặp sự cố
Khi health check báo một vùng "unhealthy", điều phối lại task của vùng đó sang vùng khỏe mạnh có queue depth thấp nhất:
def failover_check(region_health):
"""Redirect tasks from unhealthy regions."""
healthy_regions = [
r for r, h in region_health.items()
if h["status"] == "healthy"
]
if not healthy_regions:
raise RuntimeError("All regions unhealthy")
redirects = {}
for region, health in region_health.items():
if health["status"] == "unhealthy":
# Pick the healthy region with lowest queue depth
target = min(
healthy_regions,
key=lambda r: region_health[r].get("queue_depth", 0)
)
redirects[region] = target
print(f"Failover: {region} → {target}")
return redirects
Nguyên tắc failover an toàn
- Ngừng định tuyến task mới vào vùng đang xuống cấp khi độ trễ, tỷ lệ lỗi hoặc proxy vượt ngưỡng đã đặt.
- Để task đang chạy dở hoàn tất tự nhiên, không cắt ngang toàn bộ hàng đợi và phiên cùng lúc.
- Chỉ đưa traffic quay lại sau khi vùng đó vượt qua đúng health check đã dùng để quyết định failover.
Chi phí khi chạy đa vùng
| Thành phần | Yếu tố chi phí | Cách tối ưu |
|---|---|---|
| Worker instance | Tính theo compute từng vùng | Auto-scale về 0 khi không có task |
| Truyền dữ liệu liên vùng | $0,02/GB giữa các vùng | Giảm kích thước payload kết quả |
| Hàng đợi SQS | Tính theo số request | Gộp message theo batch khi có thể |
| API CaptchaAI | Giá như nhau bất kể vùng | Không phát sinh phụ phí đa vùng |
CaptchaAI tính phí theo thread, không theo vị trí worker — chạy 1 hay 5 vùng, chi phí API không đổi; chi phí tăng thêm chỉ đến từ hạ tầng.
Xử lý sự cố thường gặp
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Một vùng luôn chậm hơn hẳn | Khoảng cách vật lý tới máy chủ CaptchaAI | So sánh độ trễ baseline; chênh lệch nhỏ là bình thường |
| Router dồn hết task vào một vùng | Rule định tuyến theo domain quá rộng | Thêm rule chi tiết hơn theo TLD/subdomain |
| Failover không kích hoạt | Endpoint health check không phản hồi | Tách endpoint health khỏi logic worker |
| API key cạn nhanh hơn dự kiến | Tất cả vùng dùng chung một key | Bình thường — theo dõi usage tổng hợp |
Câu hỏi thường gặp
Triển khai đa vùng có làm tăng chi phí CaptchaAI không?
Không. CaptchaAI tính phí theo thread, không theo vị trí worker. Chi phí thêm khi mở rộng đa vùng nằm ở hạ tầng — compute, truyền dữ liệu, hàng đợi — không phải phí API.
Kiến trúc đa vùng có giúp đáp ứng residency theo Nghị định 13/2023/NĐ-CP không?
Một phần — xử lý, lưu kết quả tại vùng phát sinh dữ liệu giúp kiểm soát luồng đi, nhưng không phải tư vấn pháp lý; đối chiếu yêu cầu hệ thống bạn trước.
Nên chạy health check bao lâu một lần?
60 giây/lần đủ cho hầu hết task; khối lượng lớn hơn rút xuống 15–30 giây.
Cần tối thiểu bao nhiêu vùng để có high availability?
Hai vùng khác khu vực địa lý (ví dụ US + EU) cho HA cơ bản. Ba vùng (US + EU + Asia) mới bao phủ tính sẵn sàng toàn cầu.
Có cần API key riêng cho mỗi vùng không?
Không. Một API key dùng cho mọi vùng — theo dõi usage bằng metrics riêng, gắn nhãn region như trong code worker ở trên.
Bài viết liên quan
Bước tiếp theo
Bắt đầu với một vùng, đo round-trip thật tới target site của bạn, rồi mở rộng theo bảng quyết định ở trên — lấy API key tại đây.
Hướng dẫn liên quan: