Tutorials

CAPTCHA Çözme Performans Trendleri için Zaman Serisi Verileri

Bir çözüm sorununun asıl maliyeti sorunun kendisi değil, onu ne kadar geç fark ettiğinizdir. Cuma gecesi sessizce artan zaman aşımı oranı, pazartesi sabahı doğrulama akışınızı tıkayan bir olaya dönüşür — ve elinizde yalnızca anlık bir panel varsa "bu ne zaman başladı?" sorusuna yanıtınız olmaz. Metrikleri zaman serisi olarak sakladığınızda o soru bir sorguya dönüşür. Aşağıda ölçümü çözücünüzün içine gömüyor, veriyi Prometheus, InfluxDB veya TimescaleDB'ye yazıyor ve trendi okunur kılan sorguları kuruyoruz.

İzlemeye değer yedi CAPTCHA metriği

Metrikler ikiye ayrılır: sağlayıcı kalitesini gösterenler (çözüm oranı, gecikme, hata dağılımı) ve bütçeyi gösterenler (maliyet, bakiye, kuyruk). Tablo her birini hangi metrik tipiyle toplayacağınızı özetler.

Metrik Tip Neden izlenir
Çözüm oranı (%) Gauge Sağlayıcı kalitesindeki değişiklikleri yakalar
Çözüm gecikmesi (ms) Histogram Yavaşlamaları görür, zaman aşımı eşiği belirler
Hata koduna göre hata oranı Counter Yeni beliren hata desenlerini ortaya çıkarır
Çözüm başına maliyet ($) Gauge Bütçe takibi, anormallik tespiti
Kuyruk derinliği Gauge Kapasite planlaması
Kullanılmadan süresi dolan token'lar Counter TTL ayarı için sinyal
API bakiyesi Gauge Yeniden yükleme tetiği

Yedisini ilk günden toplamanız gerekmez: çözüm oranı ve gecikmeyle başlayın, maliyet ile bakiyeyi hacim büyüdükçe ekleyin.

Prometheus, InfluxDB ve TimescaleDB arasında nasıl seçim yaparsınız?

Kod yazmadan önce veriyi nereye yazacağınıza karar verin; sonradan taşımak çoğu zaman geçmişi kaybetmek demektir. Üçü de Grafana ile sorunsuz çalışır; fark sorgu dilinde. Sunucularınızı zaten Prometheus ile izliyorsanız metrikleri oraya eklemek en az sürtünmeli yoldur; bağımsız bir depo istiyorsanız InfluxDB, ekip SQL'e alışkınsa TimescaleDB mantıklıdır.

Özellik Prometheus InfluxDB TimescaleDB
En uygun olduğu senaryo Operasyonel izleme IoT / yüksek kardinaliteli metrikler SQL tabanlı analiz
Sorgu dili PromQL Flux SQL
Saklama Yapılandırma tabanlı Politika tabanlı PostgreSQL saklama
Grafana entegrasyonu Yerleşik Yerleşik Yerleşik
Öğrenme eğrisi Düşük Orta Düşük (SQL biliyorsanız)
Kendi sunucunuzda barındırma Evet Evet Evet (PostgreSQL uzantısı)

Maliyet eğrisini thread bütçenizle hizalama

Depoyla birlikte kararlaştırın: maliyet metriği sizin için ne anlama geliyor? Yanıt, hangi metriğe eşik uyarısı bağlayacağınızı belirler.

CaptchaAI'de fatura çözüm başına değil, eş zamanlı thread sayısına göre işler ve her thread ay boyunca sınırsız çözüm içerir. Bu, eğrinin anlamını değiştirir: captcha_solve_cost_dollars harcamadan çok planınızı ne kadar doldurduğunuzu gösterir; asıl kapasite sinyali kuyruk derinliğidir.

Somut bir örnek: İstanbul'daki bir ekip olarak e-ticaret entegrasyonunuzun ödeme adımı için QA testleri çalıştırıyorsunuz. Europe/Istanbul saat diliminde test hacminiz akşam 20.00–23.00 arasında tepe yapıyor. Kuyruk derinliği yalnızca bu üç saatte tırmanıyorsa sorun plan büyüklüğü değil zamanlamadır; işleri güne yaymak BASIC ($15/ay, 5 thread) sınırları içinde kalmanızı sağlar. Kuyruk gün boyunca yüksek kalıyorsa bu gerçek bir kapasite ihtiyacıdır: STANDARD ($30/ay, 15 thread) ya da ADVANCE ($90/ay, 50 thread). Kur dalgalanmasıyla yaşayan Türk ekipleri için aylık USD tutarın sabit kalması, kararı tahmine değil ölçüme dayandırır. captcha_api_balance_dollars metriğine eşik uyarısı bağlarsanız bakiye bitmeden yükleme yaparsınız.

Tip bazlı gecikme farklarını da katın: Cloudflare Turnstile tipik olarak 10 saniyenin altında çözülürken reCAPTCHA v2 60 saniyeye kadar sürebilir. Aynı thread sayısı tipe göre çok farklı verim üretir.

Python çözücünüze Prometheus ölçümü ekleme (Push Gateway)

Kısa ömürlü çözüm işleri için Push Gateway en pratik yaklaşımdır: worker metriği bir aracıya iter, Prometheus oradan çeker; süreç bittikten sonra bile son değerler ayakta kalır. Aşağıdaki fonksiyon görevi in.php uç noktasına gönderir, sonucu res.php üzerinden sorgular ve her sonucu sayaç, histogram ve maliyet metriğine işler.

Çözücüyü ölçümlendirin

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

push_metrics() içindeki hata yutma bilinçlidir: metrik altyapısı düştüğünde çözüm akışı durmamalıdır. Histogram kovaları da CAPTCHA sürelerine göre seçilmiştir; varsayılan HTTP kovalarını bırakırsanız P95 değeriniz anlamsız çıkar.

Trendi ortaya çıkaran PromQL sorguları

Dört sorgu panelin iskeletini kurar: saatlik başarı oranı, P95 gecikme, tipe göre hata oranı, saatlik maliyet.

# 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 ile çözüm metriklerini yazma

Metrikleri ayrı bir depoda tutmak isterseniz InfluxDB doğal seçenektir. Her çözüm tek bir ölçüm noktası olur; etiketler (type, status, error_code) filtrelemeyi, alanlar toplamayı kolaylaştırır.

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)

Flux ile aynı üç trend

Flux tarafında da saatlik pencerelerde başarı oranını, tipe göre ortalama çözüm süresini ve kümülatif maliyeti aynı veriden çıkarırsınız.

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

Node.js tarafında /metrics uç noktası açma

Çözüm mantığınız bir Node.js servisinde yaşıyorsa Push Gateway'e gerek yoktur: prom-client ile aynı sayaç ve histogramları tutar, /metrics uç noktasını açarsınız. Uzun ömürlü servisler için tercih edilen yol budur.

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);

Panel yanlış şeyi gösterdiğinde

Ölçüm de bozulur — panel boş kalmaz, yanlış rakamı güvenle gösterir.

Metrikler ulaşmıyor

Panelde düzensiz boşluklar varsa Push Gateway veri almıyordur. Ağ yolunu doğrulayın; push_metrics() hatayı bilerek yuttuğu için sorun loglara düşmez.

Yüzdelik dilimler anlamsız çıkıyor

P95 gerçeğe uymuyorsa kova sınırları iş yükünüzle örtüşmüyordur. Kovaları [5, 10, 15, 20, 30, 45, 60, 90, 120] olarak ayarlayın; varsayılan HTTP kovaları milisaniye ölçeği içindir.

Maliyet eğrisi gerçekle tutmuyor

Tek bir sabit fiyat gömdüyseniz tipler arası fark eğriyi kaydırır. Maliyeti type etiketiyle ayırın.

Sorgular yavaşladı

Kardinalite patlaması en yaygın nedendir. Etiketleri type, status ve error_code ile sınırlayın.

Sık sorulan sorular

Metrikleri ne kadar süre saklamalıyım?

Saniye çözünürlüklü ham veriyi 7 gün, saatlik toplamları 90 gün, günlük özetleri süresiz tutun. Bu kademelendirme disk maliyetini düşük tutarken kampanya dönemlerini bir yıl sonra bile karşılaştırılabilir kılar.

Push Gateway kullanmak zorunda mıyım?

Hayır. Uzun ömürlü bir serviste /metrics uç noktasını doğrudan açmak daha temizdir; Node.js örneğindeki yaklaşım budur. Push Gateway asıl olarak cron ile saniyeler içinde biten işler için gereklidir.

Kaç günlük veri birikmeden trend yorumu yapabilirim?

Günlük döngüyü görmek için en az 3–4 gün, haftalık döngü için 3 hafta gerekir. Daha kısa pencerelerde tek bir yoğun saat sizi yanlış alarma sürükler; uyarıları sabit eşiklere değil hareketli ortalamalara bağlayın.

Panelde hangi CAPTCHA tiplerini ayrı kırmalıyım?

Kullandığınız her tip için ayrı type etiketi tutun: reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile, GeeTest v3 ve görüntü/OCR farklı gecikme profilleri gösterir. Tek ortalamada topladığınızda bir tipteki bozulma diğerlerinin sağlıklı verisi içinde kaybolur.

Metrik toplamak çözüm süresini yavaşlatır mı?

Pratikte hayır — sayaç ve histogram güncellemeleri mikrosaniye ölçeğinde kalır, push hatası ise yutulur. Tek gerçek risk kardinalite patlamasıdır: pageurl gibi sınırsız değerleri asla etiket yapmayın.

Sonraki adımlar

Trend okuyabilmenin tek ön koşulu veriyi bugün toplamaya başlamaktır — CaptchaAI API anahtarınızı alın ve çözücünüzü yukarıdaki ölçüm koduyla donatın.

İlgili rehberler:

Bu makale için yorumlar devre dışı bırakılmıştır.