实战教程

CAPTCHA 的时间序列数据解决性能趋势

时间点指标告诉您现在发生了什么。时间序列数据告诉您发生了什么变化、何时发生变化以及某些内容是否有出现问题的趋势。随着时间的推移,跟踪验证码解决率、延迟和成本,以便在影响管道之前捕获性能下降情况。

追踪什么

公制 类型 为什么
解决率(%) 测量 检测提供商质量变化
求解延迟(毫秒) 直方图 发现减速、计划超时
按代码的错误率 柜台 识别新出现的错误模式
每次解决的成本(美元) 测量 预算跟踪、异常检测
队列深度 测量 容量规划
令牌在使用前已过期 柜台 TTL调谐信号
API余额 测量 重新填充触发器

Prometheus + Python(推送网关)

仪器你的解算器

import os
import time
import requests
from prometheus_client import CollectorRegistry, Counter, Histogram, Gauge, push_to_gateway

registry = CollectorRegistry()

SOLVE_TOTAL = Counter(
    "captcha_solve_total", "Total CAPTCHA solve attempts",
    ["type", "status"], registry=registry
)
SOLVE_LATENCY = Histogram(
    "captcha_solve_latency_seconds", "CAPTCHA solve latency",
    ["type"], buckets=[5, 10, 15, 20, 30, 45, 60, 90, 120],
    registry=registry
)
SOLVE_COST = Counter(
    "captcha_solve_cost_dollars", "Total cost of CAPTCHA solves",
    ["type"], registry=registry
)
API_BALANCE = Gauge(
    "captcha_api_balance_dollars", "CaptchaAI account balance",
    registry=registry
)

API_KEY = os.environ["CAPTCHAAI_API_KEY"]
PUSHGATEWAY = os.environ.get("PUSHGATEWAY_URL", "localhost:9091")


def solve_with_metrics(sitekey, pageurl, captcha_type="recaptcha_v2"):
    start = time.time()

    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1
    })
    data = resp.json()

    if data.get("status") != 1:
        SOLVE_TOTAL.labels(type=captcha_type, status="submit_error").inc()
        push_metrics()
        return {"error": data.get("request")}

    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:
            elapsed = time.time() - start
            SOLVE_TOTAL.labels(type=captcha_type, status="solved").inc()
            SOLVE_LATENCY.labels(type=captcha_type).observe(elapsed)
            SOLVE_COST.labels(type=captcha_type).inc(0.00299)
            push_metrics()
            return {"solution": result["request"]}

        if result.get("request") != "CAPCHA_NOT_READY":
            SOLVE_TOTAL.labels(type=captcha_type, status="error").inc()
            push_metrics()
            return {"error": result.get("request")}

    SOLVE_TOTAL.labels(type=captcha_type, status="timeout").inc()
    push_metrics()
    return {"error": "TIMEOUT"}


def push_metrics():
    try:
        push_to_gateway(PUSHGATEWAY, job="captcha_solver", registry=registry)
    except Exception:
        pass  # Don't fail solving because metrics push failed


def update_balance():
    resp = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "getbalance"
    })
    try:
        balance = float(resp.text)
        API_BALANCE.set(balance)
        push_metrics()
    except ValueError:
        pass

普罗米修斯查询

# Success rate over last hour
rate(captcha_solve_total{status="solved"}[1h])
/ rate(captcha_solve_total[1h]) * 100

# P95 solve latency
histogram_quantile(0.95, rate(captcha_solve_latency_seconds_bucket[1h]))

# Error rate by type
rate(captcha_solve_total{status="error"}[1h])

# Hourly cost
increase(captcha_solve_cost_dollars_total[1h])

InfluxDB + Python

写入求解指标

from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS

INFLUX_URL = os.environ.get("INFLUX_URL", "http://localhost:8086")
INFLUX_TOKEN = os.environ.get("INFLUX_TOKEN", "")
INFLUX_ORG = os.environ.get("INFLUX_ORG", "captcha")
INFLUX_BUCKET = os.environ.get("INFLUX_BUCKET", "captcha_metrics")

influx_client = InfluxDBClient(url=INFLUX_URL, token=INFLUX_TOKEN, org=INFLUX_ORG)
write_api = influx_client.write_api(write_options=SYNCHRONOUS)


def record_solve_metric(captcha_type, status, elapsed_ms, cost=0.0, error=None):
    point = (
        Point("captcha_solve")
        .tag("type", captcha_type)
        .tag("status", status)
        .field("elapsed_ms", elapsed_ms)
        .field("cost", cost)
        .field("success", 1 if status == "solved" else 0)
    )
    if error:
        point = point.tag("error_code", error)
    write_api.write(bucket=INFLUX_BUCKET, record=point)


def record_balance(balance):
    point = Point("captcha_balance").field("balance", balance)
    write_api.write(bucket=INFLUX_BUCKET, record=point)

InfluxDB 查询 (Flux)

// Success rate over last 24 hours (1-hour windows)
from(bucket: "captcha_metrics")
  |> range(start: -24h)
  |> filter(fn: (r) => r._measurement == "captcha_solve" and r._field == "success")
  |> aggregateWindow(every: 1h, fn: mean)
  |> map(fn: (r) => ({r with _value: r._value * 100.0}))
  |> yield(name: "success_rate")

// Average solve time by type
from(bucket: "captcha_metrics")
  |> range(start: -24h)
  |> filter(fn: (r) => r._measurement == "captcha_solve" and r._field == "elapsed_ms" and r.status == "solved")
  |> group(columns: ["type"])
  |> aggregateWindow(every: 1h, fn: mean)
  |> yield(name: "avg_latency")

// Cumulative cost
from(bucket: "captcha_metrics")
  |> range(start: -24h)
  |> filter(fn: (r) => r._measurement == "captcha_solve" and r._field == "cost")
  |> cumulativeSum()
  |> yield(name: "cumulative_cost")

JavaScript 实现(普罗米修斯)

const client = require("prom-client");
const axios = require("axios");

const register = new client.Registry();
const API_KEY = process.env.CAPTCHAAI_API_KEY;

const solveTotal = new client.Counter({
  name: "captcha_solve_total",
  help: "Total CAPTCHA solve attempts",
  labelNames: ["type", "status"],
  registers: [register],
});

const solveLatency = new client.Histogram({
  name: "captcha_solve_latency_seconds",
  help: "CAPTCHA solve latency",
  labelNames: ["type"],
  buckets: [5, 10, 15, 20, 30, 45, 60, 90, 120],
  registers: [register],
});

async function solveWithMetrics(sitekey, pageurl, type = "recaptcha_v2") {
  const start = Date.now();

  const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: { key: API_KEY, method: "userrecaptcha", googlekey: sitekey, pageurl, json: 1 },
  });

  if (submit.data.status !== 1) {
    solveTotal.inc({ type, status: "submit_error" });
    return { error: submit.data.request };
  }

  const captchaId = submit.data.request;

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const poll = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });

    if (poll.data.status === 1) {
      const elapsed = (Date.now() - start) / 1000;
      solveTotal.inc({ type, status: "solved" });
      solveLatency.observe({ type }, elapsed);
      return { solution: poll.data.request };
    }

    if (poll.data.request !== "CAPCHA_NOT_READY") {
      solveTotal.inc({ type, status: "error" });
      return { error: poll.data.request };
    }
  }

  solveTotal.inc({ type, status: "timeout" });
  return { error: "TIMEOUT" };
}

// Expose metrics endpoint
const express = require("express");
const app = express();
app.get("/metrics", async (req, res) => {
  res.set("Content-Type", register.contentType);
  res.end(await register.metrics());
});
app.listen(9090);

数据库比较

特征 普罗米修斯 InfluxDB 时标数据库
最适合 运营监控 物联网/高基数指标 基于 SQL 的分析
查询语言 普罗姆QL 通量 SQL
保留 基于配置 基于政策 PostgreSQL 保留
Grafana 集成 本国的 本国的 本国的
学习曲线 低的 中等的 低(如果你懂 SQL)
自托管 是的 是的 是(PostgreSQL 扩展)

故障排除

问题 原因 处理方式
仪表板中的指标差距 推送网关收不到数据 检查求解器和推送网关之间的网络
延迟直方图显示错误的百分位数 存储桶边界与工作负载不匹配 调整桶:[5, 10, 15, 20, 30, 45, 60, 90, 120] 用于验证码解决
成本指标与实际支出不符 每个验证码类型的价格不同 按类型划分的标签成本;使用实际的每种类型定价
基数过多 标签值太多 将标签限制为 typestatuserror_code

常问问题

我应该使用哪个时间序列数据库?

如果您已经使用 Prometheus 进行基础设施监控,只需添加 CAPTCHA 指标即可。如果您想要独立的指标存储,则可以使用 InfluxDB。如果您想要对时间序列数据进行 SQL 查询,则可以使用 TimescaleDB。

我应该保留 CAPTCHA 指标多久?

将高分辨率数据(每秒)保留 7 天,汇总数据(每小时)保留 90 天,并无限期保留每日摘要。这可以平衡存储成本与趋势可见性。

我可以自动检测解决率下降吗?

是的。设置滚动平均值警报 - 例如,当 1 小时成功率低于 90% 或 P95 延迟超过 45 秒时发出警报。Prometheus Alertmanager 和 InfluxDB 警报都支持此功能。

下一步

随着时间的推移跟踪您的验证码解决性能 –”获取您的 CaptchaAI API 密钥并开始收集指标。

相关指南:

该文章已禁用评论。