Hướng Dẫn Thực Hành

Điểm cuối kiểm tra tình trạng cho nhân viên giải CAPTCHA

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:

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