Trình xử lý CAPTCHA của bạn trông vẫn hoạt động — quy trình đang chạy — nhưng chưa giải quyết thành công tác vụ nào sau 10 phút. Khóa API đã hết hoặc nhân viên bị kẹt trong vòng lặp. Nếu không kiểm tra tình trạng, người điều phối của bạn sẽ tiếp tục định tuyến công việc cho một nhân viên đã chết. Điểm cuối tình trạng cho phép bộ cân bằng tải và Kubernetes phát hiện sự cố và định tuyến lại.
Ba loại kiểm tra sức khỏe
| Kiểm tra | Câu hỏi | Phản hồi thất bại |
|---|---|---|
| Sống động | Quá trình có đang chạy không? | Khởi động lại vùng chứa |
| Sẵn sàng | Nó có thể chấp nhận công việc không? | Dừng định tuyến lưu lượng truy cập |
| Phụ thuộc | Các dịch vụ ngược dòng có ổn không? | Suy thoái một cách duyên dáng |
Python: Điểm cuối sức khỏe của Flask
import requests
import time
import threading
from flask import Flask, jsonify
from dataclasses import dataclass, field
API_KEY = "YOUR_API_KEY"
RESULT_URL = "https://ocr.captchaai.com/res.php"
app = Flask(__name__)
@dataclass
class WorkerHealth:
"""Tracks worker health metrics."""
started_at: float = field(default_factory=time.monotonic)
last_solve_at: float = 0.0
total_solved: int = 0
total_failed: int = 0
consecutive_failures: int = 0
balance: float | None = None
balance_checked_at: float = 0.0
_lock: threading.Lock = field(default_factory=threading.Lock)
def record_success(self):
with self._lock:
self.total_solved += 1
self.last_solve_at = time.monotonic()
self.consecutive_failures = 0
def record_failure(self):
with self._lock:
self.total_failed += 1
self.consecutive_failures += 1
@property
def success_rate(self) -> float:
total = self.total_solved + self.total_failed
return self.total_solved / total if total > 0 else 1.0
@property
def seconds_since_last_solve(self) -> float:
if self.last_solve_at == 0:
return time.monotonic() - self.started_at
return time.monotonic() - self.last_solve_at
health = WorkerHealth()
# Thresholds
MAX_CONSECUTIVE_FAILURES = 10
MAX_SECONDS_WITHOUT_SOLVE = 600 # 10 minutes
MIN_BALANCE = 1.0
def check_balance() -> float | None:
"""Check CaptchaAI balance."""
now = time.monotonic()
# Cache balance for 60 seconds
if health.balance is not None and now - health.balance_checked_at < 60:
return health.balance
try:
resp = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "getbalance", "json": 1,
}, timeout=10).json()
health.balance = float(resp.get("request", 0))
health.balance_checked_at = now
return health.balance
except Exception:
return health.balance # Return cached value on error
@app.route("/health/live")
def liveness():
"""Liveness probe — is the process responsive?"""
return jsonify({"status": "ok", "uptime_s": int(time.monotonic() - health.started_at)}), 200
@app.route("/health/ready")
def readiness():
"""Readiness probe — can the worker accept tasks?"""
issues = []
# Check consecutive failures
if health.consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
issues.append(f"consecutive_failures={health.consecutive_failures}")
# Check time since last solve
if health.total_solved > 0 and health.seconds_since_last_solve > MAX_SECONDS_WITHOUT_SOLVE:
issues.append(f"no_solve_for={int(health.seconds_since_last_solve)}s")
# Check balance
balance = check_balance()
if balance is not None and balance < MIN_BALANCE:
issues.append(f"low_balance=${balance:.2f}")
if issues:
return jsonify({
"status": "not_ready",
"issues": issues,
"stats": {
"solved": health.total_solved,
"failed": health.total_failed,
"success_rate": round(health.success_rate, 3),
},
}), 503
return jsonify({
"status": "ready",
"stats": {
"solved": health.total_solved,
"failed": health.total_failed,
"success_rate": round(health.success_rate, 3),
"balance": balance,
},
}), 200
@app.route("/health/dependencies")
def dependencies():
"""Check upstream dependencies."""
checks = {}
# CaptchaAI API reachability
try:
resp = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "getbalance", "json": 1,
}, timeout=10)
checks["captchaai_api"] = {
"status": "ok" if resp.status_code == 200 else "degraded",
"response_ms": int(resp.elapsed.total_seconds() * 1000),
}
except Exception as e:
checks["captchaai_api"] = {"status": "down", "error": str(e)}
all_ok = all(c["status"] == "ok" for c in checks.values())
return jsonify({
"status": "ok" if all_ok else "degraded",
"checks": checks,
}), 200 if all_ok else 503
# --- Worker loop (runs in background) ---
def worker_loop():
"""Simulated CAPTCHA solving worker."""
while True:
try:
# ... solve CAPTCHA logic ...
health.record_success()
except Exception:
health.record_failure()
time.sleep(1)
threading.Thread(target=worker_loop, daemon=True).start()
JavaScript: Điểm cuối sức khỏe nhanh
const express = require("express");
const API_KEY = "YOUR_API_KEY";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
const app = express();
const health = {
startedAt: Date.now(),
lastSolveAt: 0,
totalSolved: 0,
totalFailed: 0,
consecutiveFailures: 0,
balance: null,
balanceCheckedAt: 0,
recordSuccess() {
this.totalSolved++;
this.lastSolveAt = Date.now();
this.consecutiveFailures = 0;
},
recordFailure() {
this.totalFailed++;
this.consecutiveFailures++;
},
get successRate() {
const total = this.totalSolved + this.totalFailed;
return total > 0 ? this.totalSolved / total : 1;
},
};
async function checkBalance() {
if (health.balance !== null && Date.now() - health.balanceCheckedAt < 60000) {
return health.balance;
}
try {
const url = `${RESULT_URL}?key=${API_KEY}&action=getbalance&json=1`;
const resp = await (await fetch(url)).json();
health.balance = parseFloat(resp.request);
health.balanceCheckedAt = Date.now();
return health.balance;
} catch {
return health.balance;
}
}
app.get("/health/live", (req, res) => {
res.json({ status: "ok", uptimeMs: Date.now() - health.startedAt });
});
app.get("/health/ready", async (req, res) => {
const issues = [];
if (health.consecutiveFailures >= 10) {
issues.push(`consecutive_failures=${health.consecutiveFailures}`);
}
if (health.totalSolved > 0) {
const silentMs = Date.now() - health.lastSolveAt;
if (silentMs > 600_000) {
issues.push(`no_solve_for=${Math.round(silentMs / 1000)}s`);
}
}
const balance = await checkBalance();
if (balance !== null && balance < 1.0) {
issues.push(`low_balance=$${balance.toFixed(2)}`);
}
const stats = {
solved: health.totalSolved,
failed: health.totalFailed,
successRate: Math.round(health.successRate * 1000) / 1000,
balance,
};
if (issues.length > 0) {
return res.status(503).json({ status: "not_ready", issues, stats });
}
res.json({ status: "ready", stats });
});
app.get("/health/dependencies", async (req, res) => {
const checks = {};
try {
const start = Date.now();
const url = `${RESULT_URL}?key=${API_KEY}&action=getbalance&json=1`;
const resp = await fetch(url);
checks.captchaaiApi = {
status: resp.ok ? "ok" : "degraded",
responseMs: Date.now() - start,
};
} catch (e) {
checks.captchaaiApi = { status: "down", error: e.message };
}
const allOk = Object.values(checks).every((c) => c.status === "ok");
res.status(allOk ? 200 : 503).json({
status: allOk ? "ok" : "degraded",
checks,
});
});
app.listen(8080, () => console.log("Health server on :8080"));
Cấu hình Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: captcha-worker
spec:
replicas: 3
template:
spec:
containers:
- name: worker
image: captcha-worker:latest
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 2
Mã phản hồi kiểm tra sức khỏe
| Điểm cuối | 200 | 503 |
|---|---|---|
/health/live |
Quá trình đáp ứng | Quá trình bị đóng băng - khởi động lại |
/health/ready |
Có thể chấp nhận công việc | Dừng gửi nhiệm vụ |
/health/dependencies |
Tất cả phụ thuộc đều ổn | Thượng nguồn xuống cấp |
Ngưỡng toán tử
- Sử dụng tính sẵn sàng để chặn công việc mới, tính năng hoạt động để kích hoạt khởi động lại và cân bằng các cảnh báo để nắm bắt thông lượng bị suy giảm.
- Liên kết kiểm tra tình trạng với độ sâu hàng đợi, tỷ lệ lỗi gần đây và khả năng tiếp cận phần phụ thuộc thay vì chỉ thời gian hoạt động của quy trình.
- Giữ các giá trị ngưỡng hiển thị cho người trả lời cuộc gọi để những thay đổi về tình trạng có thể thực hiện được thay vì gây ồn ào.
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Công nhân liên tục khởi động lại | Ngưỡng sống động quá thấp | Tăng failureThreshold hoặc periodSeconds |
| Nhân viên được đánh dấu là chưa sẵn sàng trong quá trình khởi động | Chưa có lượt giải nào được tính là "quá dài" | Chỉ kiểm tra seconds_since_last_solve sau lần giải đầu tiên |
| Kiểm tra số dư làm chậm điểm cuối sức khỏe | Lệnh gọi API theo mọi yêu cầu | Lưu trữ số dư bằng TTL (khuyến nghị 60 giây) |
| Bản thân điểm cuối sức khỏe gặp sự cố | Đã kiểm tra ngoại lệ chưa được xử lý | Gói mỗi tờ séc vào try/except; trở lại xuống cấp thay vì 500 |
| Âm tính giả từ kiểm tra phụ thuộc | Lỗi mạng trong quá trình kiểm tra số dư | Sử dụng các giá trị được lưu trong bộ nhớ đệm bằng phương pháp cũ trong khi xác thực lại |
Câu hỏi thường gặp
Kubernetes nên thăm dò các điểm cuối về sức khỏe với tần suất như thế nào?
Tính sống động: cứ sau 10–30 giây với ngưỡng lỗi là 3. Mức độ sẵn sàng: cứ sau 5–10 giây với ngưỡng lỗi là 2. Các cuộc thăm dò thường xuyên hơn sẽ phát hiện sự cố nhanh hơn nhưng lại tốn thêm chi phí.
Điểm cuối sức khỏe có nên đạt API CaptchaAI không?
Chỉ dành cho kiểm tra mức độ sẵn sàng và phụ thuộc và luôn lưu trữ kết quả. Đầu dò hoạt động không bao giờ được thực hiện các lệnh gọi bên ngoài - nó phải phản hồi ngay lập tức để chứng minh quy trình vẫn hoạt động.
Làm cách nào để theo dõi sức khỏe của nhiều công nhân?
Hiển thị các chỉ số sức khỏe ở định dạng Prometheus (/metrics) cùng với các điểm cuối sức khỏe. Tổng hợp các công nhân bằng bảng thông tin Grafana để xem tình trạng toàn nhóm.
bài viết liên quan
Các bước tiếp theo
Giúp nhân viên CAPTCHA của bạn sẵn sàng sản xuất —lấy khóa API CaptchaAI của bạnvà thêm điểm cuối kiểm tra tình trạng.
Hướng dẫn liên quan: