Tutorials

Поддержание соединения и HTTP/2 для более быстрых вызовов API CAPTCHA

Одно решение reCAPTCHA v2 через API CaptchaAI — это не один запрос, а 5–7: отправка на in.php и несколько опросов res.php. Если клиент открывает новое TCP- и TLS-соединение под каждый из них, вы платите 100–300 мс накладных расходов там, где они совершенно не нужны. Решение простое — держать соединение открытым (keep-alive) или перейти на мультиплексирование HTTP/2. Ниже — рабочие примеры настройки обоих вариантов в Python и Node.js с CaptchaAI.

Сколько задержки съедает новое TLS-соединение при вызовах API CAPTCHA

Типичное решение reCAPTCHA v2 через CaptchaAI выглядит так:

  • 1 запрос на отправку в in.php
  • 4–6 запросов на опрос res.php
  • итого 5–7 HTTP-запросов на одну CAPTCHA
Сценарий Расчёт Итого
Без повторного использования соединения 5 × (TCP-рукопожатие ~50 мс + TLS ~100 мс) 750 мс накладных расходов
С keep-alive 1 × (TCP + TLS) + 4 × (~5 мс на переиспользование сокета) 170 мс накладных расходов

Экономия — около 580 мс на одно решение. При 10 000 решений в день это 1,6 часа суммарной задержки, которая просто исчезает без единого изменения в логике решения.

Разница ощущается сильнее, если воркеры развёрнуты не рядом с инфраструктурой CaptchaAI: команды, которые держат серверы в европейских регионах или дата-центрах Казахстана и Центральной Азии, получают более высокий RTT до ocr.captchaai.com, а значит и более дорогое TCP+TLS-рукопожатие на каждый новый сокет. На нестабильных мобильных каналах, где переключение сети рвёт соединение чаще обычного, keep-alive и HTTP/2 экономят не 580 мс, а заметно больше.

Python: сессия requests с постоянным соединением

Библиотека requests включает keep-alive по умолчанию, если работать через объект Session, а не через разовые вызовы requests.get(). В примере ниже сессия создаётся один раз, а submit- и poll-запросы переиспользуют одно и то же TCP-соединение:

# keepalive_solver.py
import os
import time
import requests

API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")

# Create a session — reuses TCP connections across requests
session = requests.Session()
session.headers.update({"Connection": "keep-alive"})

def solve_captcha(sitekey, pageurl):
    """Solve reCAPTCHA v2 using a persistent connection."""
    # Submit — uses existing connection if available
    resp = session.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": "1",
    })
    result = resp.json()

    if result.get("status") != 1:
        raise Exception(f"Submit failed: {result.get('request')}")

    task_id = result["request"]

    # Poll — reuses the same connection
    time.sleep(15)
    for _ in range(25):
        poll = session.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": "1",
        })
        poll_result = poll.json()

        if poll_result.get("status") == 1:
            return poll_result["request"]
        if poll_result.get("request") != "CAPCHA_NOT_READY":
            raise Exception(f"Error: {poll_result.get('request')}")

        time.sleep(5)

    raise Exception("Timeout")

# Solve multiple CAPTCHAs reusing the same connection
for i in range(5):
    token = solve_captcha(
        "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
        "https://www.google.com/recaptcha/api2/demo"
    )
    print(f"Solve {i+1}: {token[:30]}...")

Python: HTTP/2 через httpx

Если нужно не просто переиспользовать одно TCP-соединение, а мультиплексировать несколько потоков запросов в нём — переходите на httpx с http2=True. Клиент открывает одно соединение и ведёт по нему несколько запросов параллельно, что особенно полезно при параллельном решении CAPTCHA:

# http2_solver.py
import os
import time
import httpx

API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
BASE_URL = "https://ocr.captchaai.com"

# HTTP/2 client with connection pooling
client = httpx.Client(http2=True, timeout=30.0)

def solve_captcha(sitekey, pageurl):
    """Solve using HTTP/2 multiplexed connections."""
    resp = client.get(f"{BASE_URL}/in.php", params={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": "1",
    })
    result = resp.json()

    if result.get("status") != 1:
        raise Exception(f"Submit failed: {result.get('request')}")

    task_id = result["request"]
    time.sleep(15)

    for _ in range(25):
        poll = client.get(f"{BASE_URL}/res.php", params={
            "key": API_KEY, "action": "get",
            "id": task_id, "json": "1",
        })
        poll_result = poll.json()

        if poll_result.get("status") == 1:
            return poll_result["request"]
        if poll_result.get("request") != "CAPCHA_NOT_READY":
            raise Exception(f"Error: {poll_result.get('request')}")

        time.sleep(5)

    raise Exception("Timeout")

# Multiple solves over a single HTTP/2 connection
for i in range(5):
    token = solve_captcha(
        "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
        "https://www.google.com/recaptcha/api2/demo"
    )
    print(f"Solve {i+1}: {token[:30]}...")

client.close()

JavaScript: Axios с keep-alive-агентами

В Node.js axios сам по себе keep-alive не включает — за это отвечают http.Agent/https.Agent с флагом keepAlive: true. Передайте такие агенты в axios.create(), и все запросы через этот инстанс начнут переиспользовать сокеты вместо того, чтобы открывать новый на каждый вызов in.php/res.php:

// keepalive_solver.js
const axios = require('axios');
const http = require('http');
const https = require('https');

const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';

// Create agents with keep-alive enabled
const httpAgent = new http.Agent({ keepAlive: true, maxSockets: 10 });
const httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 10 });

// Axios instance with persistent connections
const api = axios.create({
  baseURL: 'https://ocr.captchaai.com',
  httpAgent,
  httpsAgent,
  timeout: 30000,
});

async function solveCaptcha(sitekey, pageurl) {
  // Submit — reuses connection
  const submit = await api.get('/in.php', {
    params: {
      key: API_KEY, method: 'userrecaptcha',
      googlekey: sitekey, pageurl, json: '1',
    },
  });

  if (submit.data.status !== 1) throw new Error(submit.data.request);
  const taskId = submit.data.request;

  // Poll — reuses same connection
  await new Promise(r => setTimeout(r, 15000));
  for (let i = 0; i < 25; i++) {
    const poll = await api.get('/res.php', {
      params: { key: API_KEY, action: 'get', id: taskId, json: '1' },
    });

    if (poll.data.status === 1) return poll.data.request;
    if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
    await new Promise(r => setTimeout(r, 5000));
  }
  throw new Error('Timeout');
}

(async () => {
  for (let i = 0; i < 5; i++) {
    const token = await solveCaptcha(
      '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
      'https://www.google.com/recaptcha/api2/demo'
    );
    console.log(`Solve ${i + 1}: ${token.slice(0, 30)}...`);
  }

  // Clean up agents
  httpAgent.destroy();
  httpsAgent.destroy();
})();

HTTP/2 или HTTP/1.1 keep-alive: что выбрать для решения CAPTCHA

Оба варианта убирают повторные TCP/TLS-рукопожатия, но по-разному ведут себя под нагрузкой:

Характеристика HTTP/1.1 keep-alive HTTP/2
Повторное использование соединения Да (последовательное) Да (мультиплексное)
Параллельные потоки 1 на соединение 100+ на одном соединении
Сжатие заголовков Нет HPACK-сжатие
Снижение задержки ~60% ~70%
Нужна поддержка в браузере Нет Нет (вызовы API, не браузер)
Область применения Последовательное решение Параллельное решение

Для последовательного решения (по одной CAPTCHA за раз) хватает HTTP/1.1 keep-alive — оверхед и так минимален. Для параллельного решения (несколько задач одновременно) HTTP/2 даёт заметный выигрыш за счёт мультиплексирования нескольких потоков через одно соединение.

Как выбрать размер пула соединений

Размер пула должен соответствовать вашему уровню параллелизма, а не быть выбран наугад:

  • 1–5 параллельных решений — пул из 5 соединений
  • 5–20 — 10 соединений
  • 20–50 — 25 соединений
  • 50–100 — 50 соединений
  • 100+ — используйте HTTP/2 (1 соединение вместо пула)

Слишком большой пул впустую расходует память на простаивающие сокеты. Слишком маленький — заставляет клиент открывать новые соединения при каждом всплеске нагрузки, и весь эффект от keep-alive исчезает. Если воркеры запущены в Kubernetes и масштабируются автоматически, ориентируйтесь на пиковое число параллельных pod'ов, а не на среднее — иначе в первые же минуты автоскейлинга пул начнёт исчерпываться быстрее, чем успевает вырасти.

Диагностика проблем с keep-alive и HTTP/2

  • Соединения обрываются между опросами — тайм-аут на стороне сервера или прокси. Установите тайм-аут keep-alive > 30 с в настройках клиента.
  • Нет прироста производительности — keep-alive уже включён по умолчанию в библиотеке. Проверьте через мониторинг сети (tcpdump, вкладка Network в DevTools).
  • Ошибки connection refused — пул соединений исчерпан. Увеличьте maxSockets или снизьте уровень параллелизма.
  • HTTP/2 не согласовывается — сервер не поддерживает h2 на этом эндпоинте. Используйте резервный вариант — HTTP/1.1 keep-alive.

Частые вопросы

Сколько соединений держать в пуле, если воркеры в Kubernetes масштабируются автоматически?

Ориентируйтесь на пиковое, а не на среднее число параллельных pod'ов. Если автоскейлер поднимает новые воркеры быстрее, чем растёт пул, добавьте запас 10–20% — иначе в первые минуты скейлинга вы получите всплеск ошибок connection refused.

Даёт ли keep-alive ощутимый выигрыш при 10–20 решениях в день?

Да, хотя абсолютная экономия небольшая — около 10–15 секунд суммарно за день. Настройка ничего не стоит (несколько строк кода), поэтому имеет смысл включать её по умолчанию даже при низком объёме, особенно если тот же клиент используется и для других вызовов API.

Как убедиться, что клиент действительно согласовал HTTP/2, а не откатился на HTTP/1.1?

Проверьте вручную: curl --http2 -v https://ocr.captchaai.com/res.php и найдите в выводе строку * Using HTTP2. Если сервер не согласовал h2, клиент тихо откатится на HTTP/1.1 — это нормальное поведение, но лучше подтвердить это один раз, чем полагаться на предположение.

Почему тайм-ауты опроса растут на нестабильном мобильном соединении?

Мобильные каналы чаще сбрасывают TCP-соединение при смене сети (Wi-Fi → LTE) или потере пакетов, из-за чего keep-alive-сокет закрывается, и клиенту приходится заново согласовывать TCP и TLS перед следующим запросом к res.php. Увеличьте тайм-аут ожидания и добавьте повтор с экспоненциальной задержкой на уровне HTTP-клиента, а не только в логике опроса.

Нужно ли закрывать сессию или клиент после каждой пачки задач?

Нет. Если вы решаете CAPTCHA периодически, держите сессию (requests.Session, httpx.Client, инстанс axios с агентами) открытой между пачками — закрытие и повторное создание каждый раз сводит на нет весь эффект keep-alive. Закрывайте её только при остановке приложения.

Следующие шаги

Настройте постоянные соединения один раз — и каждая следующая интеграция с CaptchaAI станет быстрее без дополнительных усилий:

Комментарии для этой статьи отключены.