DevOps والتوسع

مراقبة CaptchaAI باستخدام Datadog: المقاييس والتنبيهات

تبدأ مراقبة CaptchaAI في Datadog من أربع إشارات تحسم استقرار خط حل الكابتشا: معدل النجاح، وزمن الحل عند النسبة المئوية p95، وتوزيع الأخطاء، ورصيد الحساب. حين ترسل هذه الإشارات إلى Datadog عبر وكيل DogStatsD، تنتقل من اكتشاف العطل بعد توقّف الأتمتة إلى تنبيه يصلك قبل أن يتأثر أي طلب فعلي. يوضّح هذا الدليل ما تُرسله من مقاييس، وكيفية دمجها في Python وNode.js، ثم بناء لوحة تحكم وضبط تنبيهات تعمل في الإنتاج.

ما المقاييس التي يجب رصدها في خط حل الكابتشا؟

حدّد أولًا المقاييس السبعة التي تكشف صحة الأتمتة. لكل مقياس نوعه في Datadog: العدّاد للأحداث التراكمية، والرسم البياني للتوزيع، والمقياس اللحظي للحالة الآنية.

المقياس النوع لماذا يهمّ؟
captcha.solve.count عدّاد إجمالي المهام المُرسلة
captcha.solve.success عدّاد الحلول الناجحة
captcha.solve.error عدّاد الحلول الفاشلة (مصنّفة حسب نوع الخطأ)
captcha.solve.latency رسم بياني الزمن من الإرسال حتى الحل
captcha.queue.depth مقياس لحظي المهام المعلّقة في قائمة الانتظار
captcha.balance مقياس لحظي رصيد الحساب المتبقي
captcha.worker.active مقياس لحظي عمليات العمّال النشطة

وسم كل مقياس بنوع الكابتشا (captcha_type) يتيح لك لاحقًا فصل أداء reCAPTCHA v2 و Cloudflare Turnstile و GeeTest v3 في اللوحة نفسها.

سيناريو من السوق: فريق مراقبة أسعار خليجي تحت الذروة

خذ فريق أتمتة في متجر إلكتروني خليجي على خطة ADVANCE ‏(90$ شهريًا، 50 خيط معالجة). في ذروة الجمعة البيضاء تمتلئ الخيوط الخمسون فتنمو captcha.queue.depth ويقفز زمن الحل عند p95؛ ومع تنبيه على هذين المقياسين يصل الإنذار خلال دقائق فيوسّع الفريق العمّال أو يرفع خطته قبل انكسار خط البيانات.

ربط CaptchaAI بـ Datadog عبر DogStatsD في Python

الطريقة الأنظف في Python مُزخرِف (decorator) يلفّ دالة الحل، فيقيس زمن كل محاولة ويصنّفها بين نجاح وخطأ، ويرسل الرصيد من res.php:

import os
import time
import functools
import requests
from datadog import initialize, statsd

# Initialize Datadog
initialize(
    statsd_host=os.environ.get("DD_AGENT_HOST", "localhost"),
    statsd_port=int(os.environ.get("DD_DOGSTATSD_PORT", "8125"))
)

API_KEY = os.environ["CAPTCHAAI_API_KEY"]
session = requests.Session()


def track_captcha_metrics(captcha_type="recaptcha_v2"):
    """Decorator to track solve metrics."""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            tags = [f"captcha_type:{captcha_type}"]
            statsd.increment("captcha.solve.count", tags=tags)

            start = time.time()
            try:
                result = func(*args, **kwargs)
                elapsed = time.time() - start

                if "solution" in result:
                    statsd.increment("captcha.solve.success", tags=tags)
                    statsd.histogram("captcha.solve.latency", elapsed, tags=tags)
                else:
                    error = result.get("error", "unknown")
                    statsd.increment(
                        "captcha.solve.error",
                        tags=tags + [f"error:{error}"]
                    )
                return result
            except Exception as e:
                statsd.increment(
                    "captcha.solve.error",
                    tags=tags + [f"error:{type(e).__name__}"]
                )
                raise
        return wrapper
    return decorator


@track_captcha_metrics(captcha_type="recaptcha_v2")
def solve_recaptcha(sitekey, pageurl):
    resp = session.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:
        return {"error": data.get("request")}

    captcha_id = data["request"]
    for _ in range(60):
        time.sleep(5)
        result = session.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": captcha_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return {"solution": result["request"]}
        if result.get("request") != "CAPCHA_NOT_READY":
            return {"error": result.get("request")}
    return {"error": "TIMEOUT"}


def report_balance():
    """Send balance as a gauge metric."""
    resp = session.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "getbalance", "json": 1
    })
    data = resp.json()
    if data.get("status") == 1:
        balance = float(data["request"])
        statsd.gauge("captcha.balance", balance)
        return balance
    return None


def report_queue_depth(depth):
    """Report current queue depth."""
    statsd.gauge("captcha.queue.depth", depth)


def report_worker_count(active, total):
    """Report worker health."""
    statsd.gauge("captcha.worker.active", active)
    statsd.gauge("captcha.worker.total", total)

إرسال المقاييس من Node.js إلى Datadog

في Node.js توفّر مكتبة hot-shots عميل StatsD متوافقًا مع DogStatsD. يوحّد prefix بادئة المقاييس، ويسم globalTags البيئة تلقائيًا لفصل الإنتاج عن التطوير:

const { StatsD } = require("hot-shots");
const axios = require("axios");

const API_KEY = process.env.CAPTCHAAI_API_KEY;

const dogstatsd = new StatsD({
  host: process.env.DD_AGENT_HOST || "localhost",
  port: parseInt(process.env.DD_DOGSTATSD_PORT || "8125", 10),
  prefix: "captcha.",
  globalTags: [`env:${process.env.NODE_ENV || "development"}`],
});

async function solveCaptchaWithMetrics(sitekey, pageurl, captchaType = "recaptcha_v2") {
  const tags = [`captcha_type:${captchaType}`];
  dogstatsd.increment("solve.count", 1, tags);
  const startTime = Date.now();

  try {
    const result = await solveCaptcha(sitekey, pageurl);
    const elapsed = (Date.now() - startTime) / 1000;

    if (result.solution) {
      dogstatsd.increment("solve.success", 1, tags);
      dogstatsd.histogram("solve.latency", elapsed, tags);
    } else {
      dogstatsd.increment("solve.error", 1, [...tags, `error:${result.error}`]);
    }

    return result;
  } catch (err) {
    dogstatsd.increment("solve.error", 1, [...tags, `error:${err.message}`]);
    throw err;
  }
}

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

  if (submitResp.data.status !== 1) {
    return { error: submitResp.data.request };
  }

  const captchaId = submitResp.data.request;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const pollResp = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });
    if (pollResp.data.status === 1) return { solution: pollResp.data.request };
    if (pollResp.data.request !== "CAPCHA_NOT_READY") {
      return { error: pollResp.data.request };
    }
  }
  return { error: "TIMEOUT" };
}

async function reportBalance() {
  try {
    const resp = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "getbalance", json: 1 },
    });
    if (resp.data.status === 1) {
      const balance = parseFloat(resp.data.request);
      dogstatsd.gauge("balance", balance);
      return balance;
    }
  } catch (err) {
    console.error("Balance check failed:", err.message);
  }
  return null;
}

// Report balance every minute
setInterval(reportBalance, 60000);

module.exports = { solveCaptchaWithMetrics, reportBalance };

بناء لوحة مراقبة CaptchaAI في Datadog

بعد تدفّق المقاييس، تحتاج لوحة تجمع الصورة في نظرة واحدة: النجاح مقابل الخطأ، وزمن الحل عند p50 و p95 و p99، والرصيد، وعمق الطابور. استورد قالب JSON التالي في Datadog:

{
  "title": "CaptchaAI Pipeline",
  "widgets": [
    {
      "definition": {
        "type": "timeseries",
        "title": "Solve Rate (Success vs Error)",
        "requests": [
          {"q": "sum:captcha.solve.success{*}.as_count()"},
          {"q": "sum:captcha.solve.error{*}.as_count()"}
        ]
      }
    },
    {
      "definition": {
        "type": "timeseries",
        "title": "Solve Latency (p50, p95, p99)",
        "requests": [
          {"q": "avg:captcha.solve.latency{*}"},
          {"q": "percentile:captcha.solve.latency{*},0.95"},
          {"q": "percentile:captcha.solve.latency{*},0.99"}
        ]
      }
    },
    {
      "definition": {
        "type": "query_value",
        "title": "API Balance",
        "requests": [{"q": "avg:captcha.balance{*}"}]
      }
    },
    {
      "definition": {
        "type": "timeseries",
        "title": "Queue Depth",
        "requests": [{"q": "avg:captcha.queue.depth{*}"}]
      }
    }
  ]
}

ضبط التنبيهات قبل توقّف خط الأنابيب

اللوحة تُظهر الحالة، لكن التنبيهات ما يوقظك في الوقت المناسب. اضبط عتبات واقعية: تحذير مبكر، وإنذار حرِج عند توقّف الخدمة. الجدول التالي مجموعة أولية جيدة:

التنبيه الشرط الشدّة
رصيد منخفض captcha.balance < 10 تحذير
رصيد حرج captcha.balance < 2 حرِج
ارتفاع معدل الأخطاء معدل الخطأ > 10% خلال 5 دقائق تحذير
قفزة في زمن الحل p95 > 120 ثانية خلال 10 دقائق تحذير
تراكم قائمة الانتظار عمق الطابور > 100 وينمو خلال 5 دقائق تحذير
توقّف العمّال captcha.worker.active == 0 حرِج

أنشئ أي تنبيه عبر واجهة Datadog البرمجية. المثال التالي يعرّف مراقب انخفاض الرصيد بصيغة YAML:

# Datadog monitor definition (API create)
- type: metric alert
  name: "CaptchaAI Low Balance"
  query: "avg(last_5m):avg:captcha.balance{*} < 10"
  message: "CaptchaAI balance is low: {{value}}. Top up to avoid solve failures."
  tags:

    - team:scraping
    - service:captcha

معالجة الأعطال الشائعة

معظم مشكلات المراقبة سببها وكيل DogStatsD أو صيغة الوسوم، لا خدمة الحل. راجع الجدول التالي عند غياب أي مقياس:

المشكلة السبب المحتمل الإجراء
المقاييس لا تظهر في Datadog وكيل DogStatsD لا يعمل أو المنفذ خاطئ تحقّق من DD_AGENT_HOST وDD_DOGSTATSD_PORT وتأكّد من تشغيل حاوية الوكيل عبر docker ps
الرسم البياني لزمن الحل فارغ لا حلول ناجحة يجري تتبّعها تأكّد من استدعاء statsd.histogram() على مسار النجاح فقط
الوسوم مفقودة أو غير قابلة للتصفية صيغة وسم خاطئة استخدم صيغة key:value دون مسافات داخل الوسم
مقاييس مكرّرة أو أرقام مضخّمة أكثر من مُبلِّغ يعمل على الرصيد شغّل مُبلِّغ رصيد واحدًا فقط لكل عملية نشر

أسئلة شائعة

كيف أفصل مقاييس كل نوع كابتشا داخل Datadog؟

مرّر captcha_type عند وسم كل مقياس، ثم صفِّ اللوحة حسبه لتقارن زمن حل reCAPTCHA v2 مقابل Cloudflare Turnstile مقابل GeeTest v3 دون خلط الأرقام.

ما القيمة المناسبة لعتبة تنبيه الرصيد؟

اضبطها على معدل استهلاكك لا برقم ثابت. القاعدة أن يمنحك حدّ التحذير مهلة يوم عمل كامل لإعادة الشحن قبل بلوغ الحدّ الحرِج.

متى يُطلق تنبيه زمن الحل عند p95؟

راقب خطك الأساسي لأسبوع، ثم اضبط العتبة أعلى من ذروتك الطبيعية بهامش معقول. عتبة 120 ثانية بداية جيدة، واخفضها إن كان زمن حلك المعتاد أقصر.

هل أحتاج وكيل Datadog على كل خادم عامل؟

لا؛ شغّل وكيل DogStatsD واحدًا لكل مضيف (host)، وترسل جميع العمّال عليه مقاييسها إلى الوكيل المحلي الذي يمرّرها إلى Datadog.


الخطوات التالية

أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.