DevOps & Scaling

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

验证码识别成功率跌到六成,你是靠告警第一时间发现,还是等客户投诉了才回头翻日志?很多接入 CaptchaAI 的团队一开始都没接监控,等余额耗尽、类型不支持、网络超时这些故障混在一起,出问题都定位不到根因。Prometheus 采集指标,Grafana 可视化并告警,是目前最主流的开源组合。本文给出可以直接落地的指标定义、导出器代码、Docker Compose 部署清单、PromQL 查询和告警规则。


需要采集哪些指标

指标名称 类型 说明
captcha_solves_total 计数器 识别请求总次数
captcha_solves_success 计数器 识别成功次数
captcha_solves_errors 计数器 失败次数(按错误类型细分)
captcha_solve_duration 直方图 识别耗时分布
captcha_balance 仪表 当前账户余额
captcha_queue_length 仪表 队列中待处理的任务数

用 Python 写一个指标导出器

下面的 metrics.pyprometheus_client 给识别逻辑加上计数器、直方图和仪表三类指标:

# 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")

solve() 会自动记录次数、耗时和错误码;update_balance() 建议丢进定时任务,每隔几分钟同步一次余额。访问 http://localhost:8000/metrics 即可看到原始指标文本。


配置 Prometheus 抓取任务

把导出器地址写进 prometheus.yml,让 Prometheus 每 10 秒抓一次:

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:

  - job_name: "captcha-solver"
    static_configs:

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

用 Docker Compose 一次性起 Prometheus + Grafana

不想分开装三套服务时,用下面的 docker-compose.yml 一条命令把 solver、Prometheus、Grafana 一起拉起来:

# 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:

启动后 Prometheus 在 :9090,Grafana 在 :3000(默认密码来自 GF_SECURITY_ADMIN_PASSWORD,上生产前记得改掉)。


Grafana 面板常用 PromQL

刚接入时不用做全部指标,先做四块就够判断"服务是否正常":成功率、平均耗时、错误分布、账户余额。规模上来后再补队列深度和吞吐量。

识别成功率

成功次数除以总次数,乘以 100:

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

平均识别耗时

_sum 除以 _count,得到窗口内平均耗时:

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

按错误类型统计

error_code 分组,快速看出余额不足、超时还是接口错误占大头:

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

余额趋势

仪表类型的指标可以直接画出余额随时间变化的曲线:

captcha_balance_usd

P95 识别耗时

P95 比平均值更能反映最差情况下的体验:

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

设置告警规则

把下面的规则加进 alert_rules.yml:余额过低、错误率过高、P95 耗时过长都能第一时间推到 IM 群或邮箱:

# 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"

国内团队部署时的几个细节

Prometheus、Grafana、solver 都部署在国内服务器(阿里云、腾讯云或自建机房)时,监控栈本身不涉及跨境访问。但 solver 调用 CaptchaAI 接口走国际链路,captcha_solve_duration 里偶尔出现的长尾耗时,很多时候是跨境网络抖动,未必是识别变慢。建议打上 method 标签,用 sum by (method) 把 reCAPTCHA、Turnstile、GeeTest v3 的耗时和成功率分开看。安装依赖慢时可加国内镜像:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple prometheus_client requests


常见故障排查

现象 原因 处理方式
/metrics 没有数据 导出器没启动 确认已调用 start_http_server(8000)
Prometheus target 显示 Down 抓取地址不对 检查 Docker 网络和端口映射
Grafana 面板没有数据 没配置 Prometheus 数据源 在 Grafana 里添加 Prometheus 数据源
服务重启后指标归零 计数器重置是预期行为 查询时用 rate(),不要直接读原始计数器

常见问题

Prometheus 监控会拖慢验证码识别本身吗?

不会。prometheus_client 每次操作开销不到 1 毫秒,10-15 秒抓取一次对识别速度没有可感知的影响。

多个 worker 同时跑,Grafana 上的成功率数字还准吗?

准。每个 worker 暴露自己的 /metrics,Prometheus 抓取所有目标,rate()sum() 默认就是跨实例聚合。

reCAPTCHA、Turnstile、GeeTest v3 混着用,怎么在同一个面板里分开看?

给调用打上 method 标签,查询时用 sum by (method) 筛选,就能把不同类型的成功率、耗时拆开对比。

LowBalance 告警阈值设成 $5,是不是太低了?

$5 只是示例值,按日均消耗调整。日均消耗 $20 左右的账户,建议提到 $30-50,避免半夜余额耗尽却没人处理。

Prometheus 的数据要保留多久,会不会把磁盘写满?

默认保留 15 天,占用通常几百 MB 到几 GB。要留更久,可在启动参数加 --storage.tsdb.retention.time=90d


相关指南


把这套监控接进你的生产环境——现在试用 CaptchaAI

该文章已禁用评论。