Головна / Статті / Чому підвищення конкурентності спричиняє помилки HTTP 429: швидкість запуску та обмеження за хостом

Чому підвищення конкурентності спричиняє помилки HTTP 429: швидкість запуску та обмеження за хостом

Збільшення кількості працівників без встановлення лімітів швидкості запуску та обмежень на одного хоста спричиняє сплески навантаження, які призводять до помилок 429. Пропускна здатність досягається завдяки контрольованій одночасній роботі, стратегії відкладених спроб та розумному проектуванню черги.

3571 слів

Коротко: Підвищення рівня конкурентності пропонується як безкоштовний паралелізм: більше працівників, більше запитів, більше даних. Десять — це гарно, тож двадцять мають бути кращими, а п’ятдесят — непереборними. Проблема в тому, що збільшення кількості ресурсів впливає не лише на конкурентність. Воно також визначає, наскільки інтенсивно клієнт звертається до хоста з самого початку, а у випадку роботи з кількома хостами — який рівень конкурентності на кожен хост виникає без будь-яких налаштувань. Коли ці приховані механізми не функціонують, код статусу часто стає HTTP 429, що означає „занадто багато одночасних запитів“. Тоді команди скорочують кількість ресурсів або використовують проксі та продовжують роботу. Не виявивши справжню причину, вони втрачають можливість досягти кращої продуктивності.

Частина I — Конкурентність проти швидкості запуску: одне налаштування пулу контролює два ліміти

Розмір пулу працівників (p-limit(n), семафор, кількість потоків) обмежує кількість операцій, які можуть виконуватися одночасно. Він не обмежує швидкість початку виконання нових завдань. У момент t=0 пул із п’ятдесятьма очікуючими завданнями може розпочати п’ятдесят запитів за один імпульс — це сплеск швидкості початку виконання — навіть якщо у стабільному режимі одночасність буде виглядати як „лише п’ятдесят“. Лімітери швидкості враховують цей початковий сплеск так само, як і стабільний паралелізм.

Вимірювання сплеску швидкості початку виконання

Допомагає інструмент для відтворення умов. Візьміть невеликий набір URL-адрес статей з Wiki Arch Linux:

https://wiki.archlinux.org/title/Arch_Linux
https://wiki.archlinux.org/title/Installation_guide
https://wiki.archlinux.org/title/Pacman
https://wiki.archlinux.org/title/Systemd
...and so on

Створіть навантаження з 100 запитами, циклічно використовуючи цей список:

function buildWorkload(urls, n) {
  return Array.from({ length: n }, (_, i) => urls[i % urls.length]);
}
async function runPooledFetch(urls, concurrencyLimit, agent, options = {}) {
  const limit = pLimit(concurrencyLimit);
  const results = await Promise.all(
    urls.map((url) => limit(() => fetchOne(url, agent)))
  );
  // ...
}

Запустіть тест із різними розмірами пулів, класифікуйте кожен результат як успішний або як помилку 429 (та інші проблеми), та фіксуйте кількість завершених запитів на секунду разом із кількістю успішних запитів. Це розрізнення має значення: навіть швидка помилка 429 впливає на показник „завершених запитів/секунду“, хоча не доставляє жодної сторінки.

Корисна пропускна здатність знижується зі зростанням глобальної конкуруентності

При фіксованому обсязі роботи у 100 запитів, де змінюється лише розмір пулу, пряме з’єднання демонструє погану продуктивність. При одночасності 1 практично всі 100 запитів виконуються успішно, а кількість завершених запитів на секунду становить близько 2,4/с — доводиться чекати на повне оброблення без можливості паралельних запусків. При одночасності 10 кількість успішних запитів значно зменшується: близько 42 сторінок обробляються успішно, а 58% запитів повертають помилку 429; кількість завершених запитів на секунду піднімається до приблизно 23,5/с. При одночасності 25 та 50 кількість успішних запитів продовжує знижуватися (близько 24/100, потім 16/100), тоді як кількість завершених запитів залишається на рівні від двадцяти до двадцяти трьох на секунду (~18,2/с та 21,8/с).

Максимум навантаження досягається при одночасності 10 зі 23,5 завершених запитів/секунду — це найвищий показник у таблиці — але разом із одним із найгірших показників кількості успішних запитів. Оптимізація під кількість завершених запитів на секунду передбачала б використання налаштувань, які споживають більшу частину ресурсів на блокування. Корисною є кількість успішних запитів на секунду, а не просто загальна кількість завершених запитів.

Додавання ліміту швидкості початку обробки при тій самій одночасності вирішує проблему

Збережіть розмір пулу без змін та встановіть обмеження щодо того, як швидко може початися наступний запит. Мінімальний інтервал визначається за допомогою максимальної кількості запитів на секунду:

// minGapMs derived from --max-rps; pool concurrency is untouched
if (minGapMs > 0) {
  const now = Date.now();
  const waitMs = Math.max(0, nextStartAt - now);
  if (waitMs > 0) await sleep(waitMs);
  nextStartAt = Math.max(Date.now(), nextStartAt) + minGapMs;
}

За допомогою контролю темпу обробки таємнича одночасність, яка раніше перевантажувала сервер, дозволяє завершити майже всі сторінки, оскільки раптовий сплеск запитів зникає. Правильна модель мислення передбачає наявність двох параметрів: одночасність (запити у процесі обробки) та швидкість початку обробки (запити, які допускаються на секунду). Об’єднання їх у одне число приховує причини збоїв.

Чому пул HTTP-клієнта надсилає хвилю запитів у момент t=0

Пули не знають про коректне регулювання частоти запитів. Вони знають лише про вільні слоти. Під час запуску кожен слот є вільним, тому кожне завдання у черзі, яке може виконуватися, дійсно виконується. Повторне використання з’єднань та мультиплексування HTTP/2 можуть зробити цю хвилю ще інтенсивнішою. Лише контролер допуску — бак токенів, мінімальний інтервал, „текучий“ бак — формує початок роботи незалежно від обмежень під час передачі даних.

Чи вирішують проксі для домашнього використання проблему раптового зростання частоти запитів?

Коректне регулювання є способом усунення проблем: збирати дані без різкого навантаження на ціль. Однак інженерні реалії іноді вимагають швидкості, яку не може забезпечити обмеження у 2,4 запиту/с. Той самий наївний підхід — відсутність обмежень частоти запитів, хвиля запитів у момент t=0 — при проходженні через проксі для домашнього використання може забезпечити приблизно 98–99/100 успішних запитів без суттєвих заборон на різних рівнях конкурентності, тоді як прямий шлях все одно призводить до серйозних проблем.

Це не означає, що проксі усувають необхідність розуміння швидкості початку обробки запитів. Вони змінюють репутацію IP-адрес та спосіб, яким сайт аналізує обсяг трафіку. Вони можуть приховати раптовий сплеск запитів, який міг би призвести до заборони використання певної IP-адреси. Якщо вимога до продукту полягає у «зборі даних без створення враження надмірного навантаження», то контроль темпу залишається основним засобом управління; проксі є лише шаром для збільшення пропускної здатності та покращення репутації, а не заміною методів вимірювання кількості запитів.

Створення проксі-агента зазвичай здійснюється шляхом отримання облікових даних з середовища:

function buildProxyAgent() {
  const user = process.env.BRIGHT_DATA_PROXY_RESIDENTIAL_USERNAME;
  const pass = process.env.BRIGHT_DATA_PROXY_RESIDENTIAL_PASSWORD;
  if (!user || !pass) return null;
  const host = process.env.BRIGHT_DATA_PROXY_HOST || 'brd.superproxy.io';
  const port = process.env.BRIGHT_DATA_PROXY_PORT || '33335';
  const proxyUrl = `http://${encodeURIComponent(user)}:${encodeURIComponent(pass)}@${host}:${port}`;
  return new HttpsProxyAgent(proxyUrl);
}
const residentialAgent = buildProxyAgent();
await runPooledFetch(tasks, 50, residentialAgent);

Використовуйте їх свідомо, фіксуйте, чи була робота здійснена безпосередньо чи через проксі, а також записуйте спостережувану швидкість початку обробки запитів, щоб знати, який фактор вплинув на результат.

Частина II — Конкурентність за хостом

Глобальні пули також приховують ще один виникаючий ліміт. Розгляньте наступне:

const limit = pLimit(50);
await Promise.all(urls.map((url) => limit(() => fetchOne(url))));

П’ятдесят глобальних слотів, розподілених між багатьма хостами, не означають, що кожен хост бачить максимум п’ятдесят запитів у процесі виконання. Порядок черги та відхилення часу відповіді визначають, скільки запитів насправді обробляє певний хост. Змішаний список може здаватися збалансованим на папері:

1. arch
2. github
3. arch
4. mdn
5. npm
6. arch
7. cloudflare

проте все одно спричиняє наплив запитів до «суворого» хоста, якщо його URL-адреси знаходяться у наборі для виконання.

Тестування паралельності за хостом

Використайте цей інструментарій з двома хостами:

  • «Суворий» хост — Arch Linux Wiki (10 URL-адрес статей), який блокується під тиском, як у Частині I.
  • «Більш лояльний» хост — сторінки каталогу books.toscrape.com (10 URL-адрес), які рідко блокуються та виконують функцію контролю. Якщо сандбокс зазнає невдачі, клієнт не працює.

Періодичний список дає 20 URL × 5 повторень = 100 запитів при глобальній одночасності 50. Списки фіксатур та інструменти їх створення знаходяться у репозиторії open concurrency-trap-bench (наприклад urls-mixed-arch-books.txt).

https://wiki.archlinux.org/title/Arch_Linux
https://books.toscrape.com/catalogue/page-1.html
https://wiki.archlinux.org/title/Installation_guide
https://books.toscrape.com/catalogue/page-2.html
https://wiki.archlinux.org/title/Pacman
https://books.toscrape.com/catalogue/page-3.html
...and so on

Впорядковані завдання можуть виконуватися за принципом round-robin, блокуватися за хостом або перемішуватися з використанням „насіння“:

function buildOrderedWorkload(urls, pattern, { repeatsPerUrl = 5, seed = null } = {}) {
  const n = urls.length * repeatsPerUrl;
  let list = Array.from({ length: n }, (_, i) => urls[i % urls.length]);
if (pattern === 'block') {
    // AxN, BxN, and so on. Repeat each URL before advancing.
    const out = [];
    for (const url of urls) {
      for (let i = 0; i < repeatsPerUrl; i++) out.push(url);
    }
    return out;
  }
if (pattern === 'shuffle') {
    // Fisher–Yates with a fixed seed so the run is reproducible
    for (let i = list.length - 1; i > 0; i--) {
      seed = (Math.imul(1664525, seed) + 1013904223) >>> 0;
      const j = seed % (i + 1);
      [list[i], list[j]] = [list[j], list[i]];
    }
  }
  // 'round-robin' leaves the alternating list as-is
return list;
}

Вимірюйте максимальну кількість запитів, що обробляються на кожному хості, за допомогою подій початку/кінця:

function maxConcurrentPerHost(results) {
  const eventsByHost = new Map();
  for (const r of results) {
    const host = new URL(r.url).hostname;
    if (!eventsByHost.has(host)) eventsByHost.set(host, []);
    const end = r.startedAt + r.ms;
    eventsByHost.get(host).push({ t: r.startedAt, delta: 1 }, { t: end, delta: -1 });
  }
  // sort events by time, sweep: +1 on start, -1 on finish, track max
}

Глобальна одночасність залишається незмінною; змінюється лише порядок виконання. Це дозволяє ізолювати навантаження на кожен хост від розміру пулу.

Як порядок черги змінює одночасність на кожному хості

Реальні сканери ніколи не дотримуються ідеального принципу round-robin постійно. Сайт-карти, графи залежностей та черги повторних спроб перерозподіляють завдання. Тому однакові глобальні налаштування можуть призводити до різних піків навантаження на кожному хості.

1. Round-robin: по черзі між хостами

Періодичний список (A B A B …) розподіляє навантаження. Пік обробки запитів на строгому хості залишається відносно помірним, оскільки інший хост постійно займає вільні ресурси.

2. Блокування: групування запитів від одного хоста

Групування всіх URL до Arch та всіх URL книг забезпечує строгому хосту тривалий період активної роботи. Пік обробки запитів на цьому хості зростає в міру збільшення загального обсягу даних, навіть якщо „конкурентність все ще становить 50“.

3. Шафлування (зерно 42): випадковий порядок

Шафлування з використанням зерна знаходиться між крайніми варіантами та імітує випадковий порядок обробки запитів у реальних умовах. Піки змінюються в залежності від зерна; урок полягає у чутливості цього процесу, а не у магічному перестановці.

Однакова глобальна конкурентність, різний тиск на кожного хоста

У всіх цих сценаріях показник успішності на строгому хості досягає свого максимуму під час обробки запитів ближче до значення, виміряного під час роботи, ніж при постійному глобальному значенні c=50. Більш лояльний хост залишається у стабільному стані. Зниження глобального резервуара для „виправлення“ ситуації з строгим хостом також негативно вплине на більш лояльного хоста та все одно не дозволить стабілізувати максимум обробки запитів у строгому хості при несприятливому порядку виконання.

Рішення: окремо обмежити кількість одночасних запитів на кожен хост

У частині I було додано ліміт на початкову швидкість обробки запитів поруч із резервуаром. У частині II додано ліміт на кожен окремий хост поруч із резервуаром – це внутрішнє обмеження на кількість запитів, які може обробляти будь-який конкретний хост.

Нагадаємо три терміни:

  • Глобальна одночасність – розмір спільного резервуара (наприклад p-limit(50)). Його встановлюєте ви.
  • Одночасність на кожен хост – те, що фактично спостерігається на конкретному хості (peak_inflight). Це вимірюється.
  • Ліміт на хост — окремий максимум, який ви визначаєте для кількості одночасних запитів, які може обробляти певний хост, що знаходиться в межах глобального пулу.
  • Якщо цього ліміту на рівні хоста немає, кожен вільний глобальний слот може бути зайнятий будь-якими URL, які є готовими до обробки — включаючи ситуацію, коли всі запити спрямовуються до одного хоста. Додавання внутрішнього ліміту дає кожному хосту власний бар’єр у черзі:

    async function runPooledFetch(urls, concurrencyLimit, agent, { perHostLimit } = {}) {
      const globalLimit = pLimit(concurrencyLimit);
      const hostLimiters = new Map();
      async function fetchWithLimits(url, execute) {
        return globalLimit(async () => {
          if (perHostLimit && perHostLimit >= 1) {
            const host = new URL(url).hostname;
            if (!hostLimiters.has(host)) hostLimiters.set(host, pLimit(perHostLimit));
            return hostLimiters.get(host)(execute);
          }
          return execute();
        });
      }
      // ...
    }
    

    Тепер кластер URL з суворими обмеженнями не може одночасно займати всі глобальні слоти. Хост із більш лояльними умовами все ще може використовувати залишкову пропускну здатність. Заборони пов’язані з тією змінною, яку ви хотіли контролювати.

    Чому спільний пул праці концентрує навантаження на одному хості

    База даних заповнює вільні місця будь-якими завданнями, які можна виконати. Швидкі хости швидко звільняють місця та беруть на себе ще більше роботи — поки група URL строгих хостів не стане здатною до спільного виконання та не успадкує пакет завдань. Розмір цього пакету залежить від порядку виконання, затримки та складу завдань — жоден з цих факторів не враховується у єдиному цілочисельному показнику конкурентності. Саме тому обмежувач на рівні кожного хоста є кращим, ніж сліпе зменшення загальної бази даних.

    Практичний чек-лист

    • Розглядайте швидкість запуску як окрему налаштування. Семафор — це не обмежувач кількості запитів на секунду. Додайте окремий ліміт швидкості запуску поруч із показником конкурентності.
    • Розглядайте конкурентність на рівні кожного хоста як окрему налаштування. Роботи з кількома хостами вимагають обмежувача на рівні кожного хоста всередині загальної бази даних.
    • Фіксуйте значення observed_start_rps та peak_inflight для кожного хоста. Ви не зможете ефективно керувати обмеженнями, які ніколи не вимірювали.
  • Оптимізуйте корисну пропускну здатність. Враховуйте показник ok-per-second, а не completed-per-second; швидкі 429s все одно вважаються збоями.
  • Точні порогові значення під час окремої роботи блогу не є переносними: механізми обмеження залежать від стану системи та залежать від часу, історії трафіку та репутації IP-адреси. Переносним рішенням є ізоляція. Збій, який виглядає як «надмірна конкурентність», може бути проблемою швидкості запуску, проблемою планування для кожного хоста або обома одночасно. Доки ці змінні не будуть розділені, скорочення кількості ресурсів усуває лише симптом — і часто неправильний.

    Читання метрик, не обманюючи себе

    Проксі для житлових чи дата-центрів перерозподіляють ідентичності. Вони можуть перетворити потік даних з однієї IP-адреси на кілька менш інтенсивних потоків. Це допомагає проектам зі збору даних та може приховати слабкі механізми контролю доступу від сервера-призначення, який обмежує кількість IP-адрес. Це не скасовує етичних та договірних зобов’язань щодо сайтів, від яких ви отримуєте дані, а також не усуває технічної необхідності розуміння власного клієнта. Коли ви контролюєте клієнта, краще спочатку налаштувати правильний темп передачі даних; використовуйте проксі лише тоді, коли продукт справді потребує вищої загальної пропускної здатності через кілька ідентичностей — та постійно вимірюйте навантаження на кожну ідентичність та кожен хост, щоб не діяти всліпу за проксі-шаром.

    Об’єднання двох частин

    Зазвичай для краулерів з обробки контенту потрібні обидва механізми контролю: глобальний пул для забезпечення безпеки ресурсів з вашого боку, ліміт початкової швидкості для дотримання ввічливості під час доступу, а також ліміти на кожен хост у випадку, коли кілька адрес користуються одним пулом. Ігнорування будь-якого з цих механізмів призводить до ситуації, яка у кодах стану HTTP „виглядає“ як конкурентність. Виправлення цього проблемного сценарію не є чимось містичним – це використання інструментів моніторингу та додаткового лімітера, спрямованого на саме ту змінну, яку ви спостерігали.

    Принципи проектування, які забезпечують справедливе порівняння

    Зберігайте ідентичну таксономію успіху під час усіх тестувань: стани „ok“, HTML, HTTP 429, інші коди 4xx/5xx, таймаути та помилки парсингу мають позначатися однаково при кожному запуску. Змінюйте лише змінну, яка перевіряється — розмір пулу, мінімальний інтервал початку, увімкнення/вимкнення проксі чи порядок у черзі. При можливості попередньо нагрійте DNS та TLS, щоб перші точки кривої не опинялися під впливом шуму від процедури холодного з’єднання, якщо лише холодний старт не є частиною експерименту.

    Повторюйте виконання завдань достатню кількість разів, щоб зменшити шум, але не настільки часто, щоб адаптивні обмежувачі сайту постійно змінювали свої налаштування під час експерименту. Якщо обмежувачі мають стан, враховуйте час доби та те, чи можуть попередні заборони все ще впливати на їхню роботу. Оприлюдніть початкові значення для випадкового порядку даних, щоб інші могли відтворити ефекти порядку.

    URL-адреси конфігурацій мають бути стабільними. Назви сторінок у Вікі та каталогу пісочниці, які зникають під час аналізу, спотворюють показники кількості документів. Зберігайте списки фіксованих елементів у системі контролю версій поруч із конфігурацією, як це робить проект concurrency-trap-bench, щоб графіки відображали відомі дані вхіду.

    Як виглядає „корисна пропускна здатність“ у конвеєрі обробки

    Системи на наступних етапах цікавляться прийнятими документами, а не кількістю з’єднань. Якщо скрейпер надсилає дані до індексатора, підраховуйте кількість індексованих документів на хвилину. Якщо він надсилає дані до бази даних цін, підраховуйте кількість перевірених записів. Узгоджуйте цілі оптимізації з потребами відповідного бізнес-підрозділу. Інакше інженери будуть максимізувати псевдометрику — кількість завершених HTTP-запитів — яка включає величезну кількість повідомлень з кодом 429.

    Повторні спроби погано взаємодіють із басейнами без контролю темпу. Раптовий сплеск, який призводить до заборони, за якою слідують негайні повторні спроби, може ще більше підвищити швидкість запуску. Слід зменшувати інтенсивність спроб при коді 429 з використанням ефекту jitter, дотримуватися значення Retry-After, якщо воно є, та ніколи не дозволяти шторму повторних спроб обійти обмеження швидкості запуску. Контролер прийому має розглядати повторні спроби як нові запуски.

    Скейлери для кількох орендарів та кількох хостів

    Сервіси, які обробляють запити від багатьох клієнтів, часто вже мають глобальні обмеження на одночасну роботу для забезпечення безпеки процесів. Проте їм все одно потрібні бюджети на кожну мету, щоб список URL одного клієнта не міг монополізувати вразливий джерело обробки. Використовуються вкладені обмеження — глобальні, за орендарем та за хостом. Їх слід реалізовувати як окремі рівні, а не сподіватися на справедливу чергування за допомогою одного семафора.

    Коли хости відрізняються за часом відгуку у кілька порядків, пули з механізмом крадіжки роботи схильні віддавати перевагу швидкому хосту. Така схильність сприяє кращій ефективності використання ресурсів, але шкодить повільному хосту, коли його URL-адреси нарешті стають доступними до виконання. Ліміти на кожного хоста обмежують ці наслідки; необов’язкові ваги для кожного хоста можуть додатково відображати принципи ввічливості.

    Інтерпретація результатів проксі без магічних припущень

    Якщо прямий вихід завершується невдачею, а вихід через проксі — успішно при ідентичних налаштуваннях пулу, ціль, ймовірно, ґрунтує свою політику на ідентичності мережі. Це корисні оперативні знання. Однак це не є доказом того, що змінилися закономірності початкової швидкості виконання. За проксі ви все одно повинні фіксувати записи про входження за кожною ідентичністю вихіду та за кожним цільовим хостом. Інакше ви просто переміщуєте сліпу зону.

    Політики щодо дотримання правил та використання роботів залишаються обов’язковими до дотримання. Вища загальна пропускна здатність через багато виходів збільшує радіус поширення логічних помилок. Регулювання швидкості функцій та обмеження на кожного хоста діють навіть при увімкнених проксі, що дозволяє знизити рівень активності без необхідності перепроектування інфраструктури ідентифікації.

    Закриття циклу від експериментів до стандартних налаштувань

    Як тільки вимірювання покажуть, що швидкість початку обробки та піки на кожного хоста краще прогнозують заборони, ніж лише загальний обсяг даних, необхідно закодувати ці результати як стандартні налаштування в бібліотеці клієнта: вимагати параметра RPS (або min-gap), встановлювати верхню межу для кожного хоста у багатохостових режимах та експортувати показники для обох варіантів. У документації слід показувати поганий дашборд (зростання кількості завершених запитів при одночасному зниженні кількості успішних) поруч із хорошим. Навчання щодо способів виникнення помилок запобігає тому, що наступна команда „виправить“ проблему паралельної обробки, що призведе до непомітного втрати корисних даних.

    Чисте відтворення експерименту з коефіцієнтом початку

    Фіксуйте версії Pin Node та undici (або вашого HTTP-стеку), щоб поведінка пулів з’єднань залишалася порівнянною. Під час тестування вимикайте несуміжні проміжкові компоненти, схожі на браузерні, які здійснюють повторні спроби. Очищуйте кеш DNS між прямими та прооксьованими запусками, якщо ваш інструментарій розрішує хости по-різному. Фіксуйте час виконання для всього набору тестів, а також часи початку та кінця кожного запиту, щоб можна було проаналізувати кількість оброблених запитів протягом перших півсекунди — це той проміжок часу, коли клієнти з пулу виглядають ідентичними, незалежно від рівня одночасних операцій, який ви вважаєте встановленим.

    Під час побудови графіків завжди відображайте кількість успішних запитів поруч із кількістю завершених запитів на секунду. Графік з подвійною осью, який приховує кількість успішних запитів, є прикладом марних показників, які вводять в оману. Експортуйте дані у форматі CSV з інструментарію, щоб інші могли перерахувати їх без необхідності покладатися на скріншоти.

    Інтерпретація суворості стилю Arch Wiki

    Хости публічної документації відрізняються: деякі обмежують швидкість за IP-адресою та шляхом, деякі — за User-Agent, деякі — за кількістю одночасних з’єднань, а деякі — за частотою запитів протягом певних періодів часу. Проблема 429 сьогодні може стати менш помітною завтра через зміни у політиці оператора. Саме тому цифри в статті є ілюстративними прикладами, а не постійними значеннями. Важливою є методологія: окремо визначати розмір пулу, швидкість прийому та максимальні навантаження на кожного хоста, а потім змінювати по одній змінній за раз.

    Якщо ви використовуєте власний суворий хост, тримайте у тому ж тестовому середовищі ще один більш лояльний хост для контролю. Якщо контрольний хост не працює, ваш клієнт також не буде функціонувати. Якщо не працює лише суворий хост, ви можете досліджувати вплив їхньої політики на ваш графік роботи.

    Деталі реалізації обмеження початкової швидкості

    Мінімальний інтервал, визначений на основі максимальної кількості запитів за секунду, є простим та ефективним для клієнтів з одним процесом. Кошики токенів дозволяють короткочасні сплески навантаження, водночас забезпечуючи дотримання середніх показників протягом тривалого часу — це корисно, коли потрібні швидкі інтерактивні запити, але водночас обережний масовий сканування. Кошики з витоками допомагають більш плавно регулювати навантаження. Який би алгоритм ви не обрали, застосовуйте його до моменту початку виконання запитів, включаючи повторні спроби. Наплив повторних запитів, який обходить ці обмеження, відтворює ситуацію масових запитів, що виникала при t=0 після першого заборонення.

    Багатопроцесні сканери потребують розподіленого механізму контролю доступу (Redis, etcd або центральний планувальник). Одних лише локальних обмежень недостатньо для координації п’яти працівників, кожен з яких вважає, що може відправляти по два запити на секунду.

    Деталі реалізації обмежень на один хост

    Звичайною схемою є використання вкладених семафорів, ключами до яких є імена хостів: спочатку отримується доступ до глобального семафора, потім — до семафора конкретного хоста, після цього виконується запит, а наостанок — звільнення ресурсів у зворотному порядку. Вирішіть, чи ділить ключ www та apex один і той самий ключ. Визначте, як перенаправлення, що змінюють кількість хостів, впливають на межі їх використання. Шари одного сайту, які базуються на певних шляхах, зазвичай все одно ділять один бюджет хоста, якщо немає чітких доказів від CDN про інше.

    Виводьте метрики: inflight_global, inflight_per_host{host}, admissions_per_second, http_429_total{host}. Створюйте попередження, коли зростає кількість запитів 429, а не лише тоді, коли вичерпується бюджет на обробку помилок типу 5xx.

    Порядок черг у продакшн-скедуляторах

    Порядок карти сайту, алгоритм BFS від початкової точки, черги пріоритету для „важливих“ URL та черги повторних спроб — усе це змінює форму пікових навантажень на кожному хості. Черга повторних спроб, яка додає на початок список невдалих URL Arch, може випадково відновити порядок блоків після часткової перерви у роботі. Справедлива чергування між хостами — черги з алгоритмом round-robin на кожному хості — зменшує випадкову концентрацію навантажень ще до встановлення жорстких обмежень. Жорсткі обмеження залишаються необхідними, коли сама справедливість не може стримати піки через нерівномірну затримку.

    Проксі без самообману

    Домашні мережі змінюють розподіл ідентичностей. Вони не скасовують закони фізики: якщо кожна ідентичність все одно починає п’ятдесят запитів одночасно, сервери, які орієнтуються на поведінку, а не на IP-адресу, все ще можуть заблокувати їх. Фіксуйте дані про входження за кожною ідентичністю виходу. Періодично змінюйте ідентичності. Дотримуйтесь правил для роботів та умов контракту. Використовуйте стримані темпи навантаження навіть при увімкнених проксі, щоб помилки не призводили до широкомасштабних наслідків.

    Проксі дата-центрів є дешевшими та легше піддаються ідентифікації; проксі для домашнього використання коштують дорожче та створюють етичні проблеми. Вибирайте уважно; не вважайте «проксі» синонімом до рішення проблем.

    Вбудуйте два необхідні параметри у загальні обгортки HTTP-клієнта, які використовуються краулерами: maxInFlight та maxStartsPerSecond, а також maxInFlightPerHost, якщо є більше одного хоста. Утримуйтеся від створення клієнта для режиму кількох хостів без обмежень на кожного хоста. Надавайте шаблони панелей керування, які відображають кількість успішних запитів на секунду. Навчайте користувачів, використовуючи два графіки: один показує ефективність при високій паралельності, а інший — втрату корисних сторінок.

    • Встановлюйте обмеження на кількість запусків, а не лише на активні з’єднання.
    • Встановлюйте обмеження на кожного хоста в межах загального пулу.
    • Вимірюйте кількість прийнятих запитів та піки навантаження на кожного хоста під час кожної роботи.
  • Оцінюйте успіх за корисними сторінками, а не за завершенням з’єднань.
  • Застосовуйте контроль доступу до спроб повторного виконання.
  • У змішаних тестах використовуйте більш лояльний контрольний хост.
  • Розглядайте успіх проксі як зміну репутації, а не доказ того, що планування працює належним чином.
  • Переглядайте цифри, коли змінюються правила для пунктів призначення; зберігайте методологію.
  • Доки ці звички не стануть нормою, команди будуть продовжувати «виправляти проблеми з конкурентністю», що призводить до менш помітних форм провалу, які все одно повертають код HTTP 429 та спалюють бюджети на сканування.