DevOps 与扩展

使用 Prometheus 和 Grafana 监控验证码解决率

生产验证码解决需要可观察性。 Prometheus 收集指标,Grafana 将其可视化,并在问题影响管道之前发出警报。


要跟踪的指标

公制 类型 目的
captcha_solves_total 柜台 总解决尝试次数
captcha_solves_success 柜台 成功解决
captcha_solves_errors 柜台 失败的解决(按错误类型)
captcha_solve_duration 直方图 求解时间分布
captcha_balance 测量 经常账户余额
captcha_queue_length 测量 队列中待处理的任务

Python 指标导出器

# metrics.py
import time
import requests
from prometheus_client import (
    Counter, Histogram, Gauge, start_http_server,
)


# Define metrics
SOLVES_TOTAL = Counter(
    "captcha_solves_total",
    "Total CAPTCHA solve attempts",
    ["method"],
)

SOLVES_SUCCESS = Counter(
    "captcha_solves_success",
    "Successful CAPTCHA solves",
    ["method"],
)

SOLVES_ERRORS = Counter(
    "captcha_solves_errors",
    "Failed CAPTCHA solves",
    ["method", "error_code"],
)

SOLVE_DURATION = Histogram(
    "captcha_solve_duration_seconds",
    "CAPTCHA solve duration in seconds",
    ["method"],
    buckets=[5, 10, 15, 20, 30, 45, 60, 90, 120],
)

BALANCE = Gauge(
    "captcha_balance_usd",
    "Current CaptchaAI account balance in USD",
)

QUEUE_LENGTH = Gauge(
    "captcha_queue_length",
    "Number of pending CAPTCHA tasks",
)


class InstrumentedSolver:
    """Solver with Prometheus metric instrumentation."""

    def __init__(self, api_key):
        self.api_key = api_key
        self.base = "https://ocr.captchaai.com"

    def solve(self, method, **params):
        """Solve CAPTCHA with metric collection."""
        SOLVES_TOTAL.labels(method=method).inc()
        start = time.time()

        try:
            token = self._do_solve(method, params)
            duration = time.time() - start

            SOLVES_SUCCESS.labels(method=method).inc()
            SOLVE_DURATION.labels(method=method).observe(duration)

            return token

        except Exception as e:
            error_code = str(e)[:30]
            SOLVES_ERRORS.labels(
                method=method, error_code=error_code,
            ).inc()
            raise

    def update_balance(self):
        """Fetch and update balance metric."""
        resp = requests.get(f"{self.base}/res.php", params={
            "key": self.api_key,
            "action": "getbalance",
            "json": 1,
        }, timeout=15)
        balance = float(resp.json()["request"])
        BALANCE.set(balance)
        return balance

    def _do_solve(self, method, params, timeout=120):
        data = {"key": self.api_key, "method": method, "json": 1}
        data.update(params)

        resp = requests.post(
            f"{self.base}/in.php", data=data, timeout=30,
        )
        result = resp.json()

        if result.get("status") != 1:
            raise RuntimeError(result.get("request"))

        task_id = result["request"]
        start = time.time()

        while time.time() - start < timeout:
            time.sleep(5)
            resp = requests.get(f"{self.base}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1,
            }, timeout=15)
            data = resp.json()
            if data["request"] != "CAPCHA_NOT_READY":
                if data.get("status") == 1:
                    return data["request"]
                raise RuntimeError(data["request"])

        raise TimeoutError("Solve timeout")


# Start metrics server on port 8000
start_http_server(8000)
print("Metrics server running on :8000/metrics")

普罗米修斯配置

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:

  - job_name: "captcha-solver"
    static_configs:

      - targets: ["solver-app:8000"]
    scrape_interval: 10s

Docker 组合堆栈

# docker-compose.yml
version: "3.8"

services:
  solver:
    build: .
    environment:

      - CAPTCHAAI_KEY=${CAPTCHAAI_KEY}
    ports:

      - "8000:8000"

  prometheus:
    image: prom/prometheus:latest
    volumes:

      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:

      - "9090:9090"

  grafana:
    image: grafana/grafana:latest
    ports:

      - "3000:3000"
    environment:

      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:

      - grafana-data:/var/lib/grafana

volumes:
  grafana-data:

Grafana 仪表板查询

成功率 (PromQL)

rate(captcha_solves_success[5m])
/ rate(captcha_solves_total[5m]) * 100

平均求解时间

rate(captcha_solve_duration_seconds_sum[5m])
/ rate(captcha_solve_duration_seconds_count[5m])

按类型划分的错误率

sum by (error_code) (
  rate(captcha_solves_errors[5m])
)

随着时间的推移保持平衡

captcha_balance_usd

P95 解决持续时间

histogram_quantile(0.95,
  rate(captcha_solve_duration_seconds_bucket[5m])
)

报警规则

# alert_rules.yml
groups:

  - name: captcha-alerts
    rules:

      - alert: LowBalance
        expr: captcha_balance_usd < 5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "CaptchaAI balance below $5"

      - alert: HighErrorRate
        expr: |
          rate(captcha_solves_errors[5m])
          / rate(captcha_solves_total[5m]) > 0.1
        for: 10m
        labels:
          severity: critical
        annotations:
          summary: "CAPTCHA error rate above 10%"

      - alert: SlowSolveTime
        expr: |
          histogram_quantile(0.95,
            rate(captcha_solve_duration_seconds_bucket[5m])
          ) > 60
        for: 15m
        labels:
          severity: warning
        annotations:
          summary: "P95 solve time exceeds 60s"

故障排除

问题 原因 处理方式
/metrics 没有指标 服务器未启动 致电start_http_server(8000)
普罗米修斯显示“向下” 目标地址错误 检查 Docker 网络和端口
Grafana显示没有数据 Prometheus 未添加为源 在Grafana中添加Prometheus数据源
重启时指标重置 预期计数器重置 使用 rate() 而非原始计数器

常问问题

Prometheus 增加了多少开销?

微不足道。 prometheus_client 库为每个公制操作增加 <1ms。每 10-15 秒抓取一次不会产生任何有意义的影响。

我可以将其与多个工作实例一起使用吗?

是的。每个工作人员都会公开自己的 /metrics 端点。普罗米修斯刮掉了所有目标。 Grafana 查询会自动聚合所有实例。

我应该从哪个仪表板开始?

从四个面板开始:成功率、平均解决时间、错误细分和平衡。随着规模的扩展,添加队列深度和吞吐量。


相关指南


完全可观察性——监控CaptchaAI今天与普罗米修斯一起。

该文章已禁用评论。