Accueil / Articles / Pourquoi l’augmentation de la concurrence provoque des erreurs HTTP 429 : taux de démarrage et limites par hôte

Pourquoi l’augmentation de la concurrence provoque des erreurs HTTP 429 : taux de démarrage et limites par hôte

Augmenter le nombre d’employés sans définir de taux de démarrage ni de limites par hôte provoque des pics qui entraînent des erreurs 429. La productivité provient d’une concurrence contrôlée, de mécanismes de retentissement et d’un design de file d’attente efficace.

3571 mots

En résumé : L’augmentation de la concurrence est présentée comme un parallélisme gratuit : plus d’agents, plus de requêtes, plus de données. Dix semble être un bon chiffre, donc vingt doit être encore meilleur, et cinquante irrésistible. Le problème, c’est que l’augmentation du pool n’affecte pas seulement la concurrence. Elle détermine également l’intensité avec laquelle le client sollicite un hôte dès le début, et — dans les charges de travail multi-hôtes — quelle concurrence par hôte se crée sans que personne ne l’ait définie. Lorsque ces mécanismes cachés échouent, le code d’état est souvent HTTP 429, ce qui correspond exactement à « trop de requêtes simultanées ». Les équipes réduisent alors le pool ou ajoutent des proxies avant de passer à autre chose. En ne ciblant pas la vraie cause, elles perdent en efficacité réelle.

Partie I — Concurrence vs taux de démarrage : une seule configuration du pool contrôle deux limites

La taille de la pool de travail (p-limit(n), un sémafor, le nombre de threads) limite le nombre d’opérations qui peuvent être en cours. Elle ne limite pas la vitesse à laquelle de nouveaux travaux peuvent commencer. À t=0, une pool vide contenant cinquante tâches en attente peut lancer cinquante requêtes en une seule période de temps — ce pic de taux de démarrage — même si la concurrence en état stationnaire semblera par la suite « n’être que de cinquante ». Les limiteurs de débit tiennent autant compte de cette poussée initiale que du parallélisme soutenu.

Mesurer le pic de taux de démarrage

Un outil reproductible est utile. Prenez un petit ensemble d’URLs d’articles de la Wiki d’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

Créez une charge de travail de 100 requêtes en itérant sur la liste :

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)))
  );
  // ...
}

Exécutez avec différentes tailles de pool, classez chaque résultat comme « ok » ou 429 (ainsi que d’autres échecs), et enregistrez le nombre de requêtes terminées par seconde ainsi que le compteur des résultats « ok ». Cette distinction est importante : un 429 rapide contribue néanmoins aux métriques trompeuses de « requêtes terminées par seconde » sans pour autant afficher de page.

Le débit utile diminue à mesure que la concurrence globale augmente

Avec une charge de travail fixée à 100 requêtes et uniquement la taille du pool qui change, une connexion directe se comporte de manière très défavorable. À une concurrence de 1, presque toutes les 100 requêtes réussissent et le nombre de requêtes terminées par seconde est proche de 2,4/s — on doit attendre la fin du traitement complet sans possibilité de démarrage en parallèle. À une concurrence de 10, les données utiles chutent : environ 42 pages réussissent et 58 % des tentatives renvoient une erreur 429, tandis que le nombre de requêtes terminées par seconde monte à environ 23,5/s. Aux concurrences de 25 et 50, le nombre de requêtes réussies continue de diminuer (environ 24/100, puis 16/100) tandis que le nombre de requêtes terminées par seconde reste dans les environs de 18 à 22 par seconde (~18,2/s et 21,8/s).

Le pic de charge correspond à une concurrence de 10 avec 23,5 requêtes terminées par seconde — le chiffre le plus élevé du tableau — associé à l’un des pires taux de réponses positives. L’optimisation en fonction du nombre de requêtes terminées par seconde recommanderait une configuration qui consomme la majeure partie du budget alloué aux interdictions. Le débit utile se mesure en réponses positives par seconde, et non en nombre brut de requêtes terminées.

Imposer une limite de vitesse de démarrage à la même concurrence résout le problème

Conservez la taille du pool inchangée et contrôlez le délai avant que la prochaine requête ne puisse commencer. Déterminez un intervalle minimum à partir d’une limite maximale de requêtes par seconde :

// 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;
}

Avec une régulation adaptée, la même concurrence qui inondait précédemment l’hôte peut maintenant finaliser presque toutes les pages, car l’afflux initial disparaît. Le modèle mental correct consiste à utiliser deux paramètres : la concurrence (en cours d’exécution) et le taux de démarrage (demandes acceptées par seconde). Les fusionner en un seul chiffre masque les modes de défaillance.

Pourquoi un client HTTP en pool envoie une série de requêtes à t=0

Les pools ne connaissent pas le concept de rythme respectueux. Ils ne connaissent que les slots disponibles. Au démarrage, chaque slot est libre, donc toutes les tâches en file d’attente pouvant s’exécuter le font réellement. La réutilisation des connexions et le multiplexage HTTP/2 peuvent encore intensifier cette série de requêtes sur le réseau. Seul un contrôleur d’accès — type bucket à jetons, intervalle minimal ou bucket fuyant — permet de réguler les débuts indépendamment des limites en cours d’exécution.

Les proxies résidentiels corrigent-ils l’effet de pic de débit au démarrage ?

Le rythme contrôlé constitue la solution pour assurer la correction : collecter des données sans surcharger la cible. La réalité technique exige parfois une vitesse que des limites de 2,4 req/s ne peuvent pas assurer. La même approche naïve — sans limite de débit au démarrage, avec un pic à t=0 — lorsqu’elle passe par des proxies résidentiels permet d’obtenir environ 98–99/100 de réussites sans quasi aucune interdiction, quel que soit le niveau de concurrence, tandis que la voie directe continue de rencontrer des problèmes majeurs.

Cela ne signifie pas pour autant que les proxies éliminent la nécessité de comprendre le taux de démarrage. Ils modifient la réputation de l’IP ainsi que la manière dont un site attribue le trafic. Ils peuvent masquer une vague de requêtes qui entraînerait l’interdiction d’une seule IP de sortie. Si l’exigence du produit est de « collecter des données sans jamais donner l’impression d’une ruée », le contrôle du rythme reste la méthode principale ; les proxies constituent une couche de capacité et de réputation, mais ne remplacent pas la mesure des admissions.

La construction de l’agent proxy tire généralement les identifiants nécessaires de l’environnement :

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

Utilisez-les de manière intentionnelle, enregistrez si une exécution s’est faite directement ou via un proxy, et notez toujours le taux de démarrage observé afin de savoir quel facteur a influencé le résultat.

Partie II — Concurrence par hôte

Les pools globaux cachent également une deuxième limite émergente. Considérez :

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

Cinquante emplacements mondiaux partagés entre de nombreux hôtes ne signifient pas pour autant que chaque hôte n’affiche au maximum que cinquante requêtes en cours d’exécution. L’ordre de la file d’attente et les écarts de temps de réponse déterminent le nombre de requêtes que chaque nom d’hôte absorbe réellement. Une liste mixte peut sembler équilibrée sur le papier :

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

pourtant elle peut encore provoquer des pics de charge sur l’hôte strict lorsque ses URL se trouvent dans l’ensemble des requêtes exécutables.

Benchmarking de la concurrence par hôte

Réutilisez l’outil avec deux hôtes :

  • Un hôte strict — Arch Linux Wiki (10 URLs d’articles) qui bloque sous pression, comme dans la partie I.
  • Un hôte tolérant — les pages de catalogue de books.toscrape.com (10 URLs) qui bloquent rarement, servant de contrôle. Si le environnement isolé échoue, c’est que le client est défectueux.

Une liste alternée génère 20 URL × 5 répétitions = 100 requêtes avec une concurrence globale de 50. Les listes de fichiers et les outils de création se trouvent dans le répertoire open concurrency-trap-bench (par exemple 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

Les charges de travail ordonnées peuvent fonctionner en mode round-robin, être bloquées par hôte ou être mélangées à l’aide d’une graine :

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

Mesurez le pic de travail en cours par hôte à partir des événements de début et de fin :

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
}

La concurrence globale reste fixe ; seule l’ordre change. Cela isole la pression sur chaque hôte de la taille du pool.

Comment l’ordre de la file modifie la concurrence par hôte

Les crawlers réels ne suivent jamais à jamais un mode round-robin parfait. Les sitemaps, les graphes de dépendance et les files de réessai réorganisent le travail. Ainsi, des paramètres globaux identiques peuvent produire des pics différents par hôte.

1. Round-robin : alternance égale des hôtes

La liste alternée (A B A B …) répartit le travail. Le pic de charge en cours d’exécution sur l’hôte strict reste relativement modéré, car l’autre hôte continue de réserver des slots.

2. Blocage : regroupement des requêtes provenant du même hôte

En groupant d’abord toutes les URLs Arch puis toutes les URLs de livres, on donne à l’hôte strict une longue séquence d’exécutions consécutives. Le pic de charge sur cet hôte augmente en fonction de la taille totale du pool, même si « la concurrence reste à 50 ».

3. Mélange (graine 42) : ordre aléatoire

Un mélange basé sur une graine se situe entre les deux extrêmes et reflète l’ordre aléatoire observé en production. Les pics varient en fonction de la graine ; l’enseignement réside dans la sensibilité à cette variable, et non dans une permutation magique.

Même concurrence globale, pression différente par hôte

À travers ces schémas, le taux d’acceptation sur le profil de hôte strict atteint son pic en cours d’exécution de manière plus fidèle que le seuil global constant c=50. Le hôte laxiste reste en bon état. Réduire le pool global pour « corriger » le hôte strict pénaliserait également celui qui est laxiste, sans pour autant permettre de maîtriser le pic du hôte strict en cas d’ordre défavorable.

Solution : limiter séparément la concurrence par hôte

La première partie a ajouté une limite de taux de démarrage en plus du pool. La deuxième partie ajoute une limite par hôte : une contrainte imbriquée déterminant le nombre de requêtes en cours d’exécution que peut gérer un nom de hôte donné.

Rappelons les trois termes :

  • Concurrence globale — taille du pool partagé (par exemple p-limit(50)). C’est vous qui la définissez.
  • Concurrence par hôte — ce que voit réellement un hôte (peak_inflight). C’est vous qui la mesurez.
  • Limite par hôte — une limite maximale distincte que vous choisissez pour le nombre d’appels simultanés qu’un nom de hôte donné peut gérer, intégrée au sein du pool global.
  • Si cette restriction au niveau de l’hôte fait défaut, chaque emplacement global libre peut être occupé par les URLs qui se trouvent prêtes — ce qui peut entraîner une surcharge vers un seul hôte strict. L’ajout de cette limite imbriquée permet à chaque nom de hôte d’avoir sa propre porte de file d’attente :

    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();
        });
      }
      // ...
    }
    

    Désormais, un groupe d’URLs liées à des hôtes stricts ne peut pas occuper tous les emplacements globaux en même temps. L’hôte plus souple peut toujours utiliser la capacité restante. Les interdictions sont liées à la variable limitée que vous souhaitez contrôler.

    Pourquoi un pool de travail partagé concentre la charge sur un hôte

    Le pool remplit les slots avec tout ce qui peut être exécuté. Les hôtes rapides libèrent rapidement leurs slots et s’attribuent davantage de travail — jusqu’à ce qu’un groupe d’URLs d’hôtes stricts devienne exécutable ensemble et hérite d’une charge soudaine. La taille de cette charge dépend de l’ordre d’exécution, de la latence et du mélange des tâches — éléments qui ne figurent pas dans l’entier unique de concurrence. C’est pourquoi un limiteur par hôte est préférable à une réduction aveugle du pool global.

    Une liste de contrôle pratique

    • Considérez le taux de démarrage comme une configuration distincte. Un sémafor n’est pas un limiteur de RPS. Ajoutez une limite explicite pour le taux de démarrage en plus de celle relative à la concurrence.
    • Considérez la concurrence par hôte comme une configuration distincte. Les charges de travail multi-hôtes nécessitent un limiteur par hôte au sein du pool global.
    • Enregistrez observed_start_rps ainsi que le peak_inflight par hôte. On ne peut pas évaluer des limites qu’on n’a jamais mesurées.
  • Optimisez le débit utile. Préférez le score en opérations correctes par seconde plutôt qu’en opérations terminées par seconde ; des temps rapides de 429 secondes restent considérés comme des échecs.
  • Les seuils exacts lors d’une exécution de blog donnée ne sont pas portables : les mécanismes de limitation dépendent de l’état du système ainsi que du temps, de l’historique du trafic et de la réputation IP. Le modèle portable consiste à isoler les éléments. Un échec qui semble dû à une « concurrence excessive » peut en réalité résulter d’un problème de taux de démarrage, d’un problème de planification par hôte, ou des deux à la fois. Tant que ces variables ne sont pas séparées, réduire le pool ne fait qu’atténuer un symptôme — et souvent le mauvais.

    Lire les métriques sans se tromper soi-même

    Le nombre de demandes terminées par seconde augmente chaque fois que le client parvient à ouvrir des sockets rapidement, même lorsque la plupart des réponses sont des refus. Les tableaux de bord qui mettent en avant la concurrence sans filtre de validation recommanderont des paramètres défavorables. Associez chaque graphique de débit à un taux de validations réussies et, si possible, au nombre d’octets de contenu utile conservés. Si votre pipeline réessaie en cas d’erreur 429, comptez ces tentatives séparément afin qu’une série de réessais ne soit pas interprétée comme un parallélisme productif.

    Les journaux de taux d’ouverture doivent enregistrer la date et l’heure de chaque admission, et non seulement celle de chaque terminaison. À partir des admissions, vous pouvez reconstituer les pics dans les 100 à 500 ms suivants, période durant laquelle de nombreux clients en pool semblent identiques, quel que soit la taille du pool configurée. Si deux configurations présentent le même pic d’ouverture, ne soyez pas surpris qu’elles suivent également le même schéma d’interdiction.

    Proxies, réputation et transparence sur les compromis

    Les proxies résidentiels ou de centre de données redistribuent les identités. Ils peuvent transformer une vague d’IP unique en de nombreux flux plus discrets. Cela facilite les projets de collecte et permet de cacher un mauvais contrôle d’accès à une destination qui impose des limites sur les IP. Cela ne supprime pas les obligations éthiques et contractuelles envers les sites que vous consultez, ni le besoin technique de comprendre votre propre client. Préférez d’abord réguler le rythme lorsque vous contrôlez le client ; utilisez des proxies lorsque le produit exige réellement un débit global plus élevé à travers plusieurs identités — et continuez à mesurer la pression par identité et par hôte afin de ne pas agir à l’aveugle derrière la couche de proxy.

    Associer les deux éléments

    Les crawlers de production ont généralement besoin des deux types de limites : un pool global pour assurer la sécurité des ressources de votre côté, une limite de vitesse de démarrage pour être courtois au moment de l’accès, ainsi que des limites par hôte lorsque plusieurs destinations partagent ce pool. Omettre l’une de ces limites crée un mode de défaillance qui « ressemble à une concurrence » dans les codes d’état HTTP. La solution n’est pas mystérieuse ; il s’agit d’instruments de suivi associés à une deuxième limite ciblant la variable que vous avez réellement observée.

    Notes de conception pour des comparaisons équitables

    Assurez-vous que la taxonomie des succès reste identique lors de toutes les exécutions : les codes HTML valides, HTTP 429, ainsi que les autres codes 4xx/5xx, les temps d’attente et les échecs de parsing doivent être étiquetés de la même manière à chaque fois. Modifiez uniquement la variable testée — taille du pool, intervalle de démarrage minimal, activation/désactivation du proxy ou ordre dans la file d’attente. Utilisez autant que possible des DNS et TLS préchauffés afin que les premiers points d’une courbe ne soient pas influencés par des problèmes liés aux échanges initiaux, sauf si un démarrage en mode froid fait explicitement partie de l’étude.

    Reproduisez les charges de travail suffisamment de fois pour atténuer le bruit, mais pas au point que le limiteur adaptatif d’un site ne change définitivement de comportement au cours de l’expérience. Lorsque les limiteurs conservent un état, notez l’heure de la journée et vérifiez si des interdictions antérieures pourraient encore avoir un effet. Publiez les valeurs de départ utilisées pour le mélange des données afin que d’autres puissent reproduire les effets liés à l’ordre.

    Les adresses URL des fichiers de référence doivent être stables. Les titres de wiki et les pages du catalogue de l’aire de test qui disparaissent au cours d’une étude faussent le comptage des fichiers valides. Placez les listes de fichiers verrouillés dans le système de contrôle de version à côté du dossier contenant les fichiers, comme le fait le projet concurrency-trap-bench, afin que les graphiques fassent référence à des entrées connues.

    À quoi ressemble un « débit utile » dans une chaîne de traitement

    Les systèmes en aval se soucient des documents acceptés, et non du nombre de requêtes échangées. Si un outil d’extraction alimente un indexeur, comptez les documents indexés par minute. S’il alimente une base de données de prix, comptez les lignes validées. Alignez l’objectif d’optimisation avec cette unité commerciale. Sinon, le département ingénierie maximisera une métrique indirecte — les échanges HTTP terminés — qui inclut de nombreux en-têtes de réponse 429.

    Les tentatives de réessai interagissent mal avec des pools non synchronisés. Une vague d’activités entraînant des bannissements, suivie de tentatives de réessai immédiates, peut encore accroître le taux d’initialisation. Appliquez un délai d’attente en cas de réponse 429 avec un léger jitter, respectez l’en-tête Retry-After lorsqu’il est présent, et empêchez à tout prix que des tempêtes de réessai contournent le contrôle du taux d’initialisation. Le contrôleur d’admission doit considérer les tentatives de réessai comme de nouvelles demandes d’initialisation.

    Planificateurs multi-locataires et multi-hôtes

    Les services qui gèrent plusieurs clients disposent souvent déjà de plafonds de concurrence globaux pour assurer la sécurité des processus. Ils ont néanmoins besoin de budgets par destination afin que la liste d’URLs d’un client ne monopolise pas une source fragile. Des limites imbriquées — globales, par locataire, par hôte — s’additionnent. Mettez-les en œuvre sous forme de couches explicites plutôt que d’espérer qu’une file d’attente équitable émerge d’un seul sémafor.

    Lorsque les hôtes présentent des différences d’ordre de grandeur en termes de latence, les groupes de partage de travail penchent en faveur de l’hôte rapide. Ce biais est avantageux pour l’utilisation des ressources mais défavorable à l’hôte lent lorsque ses URL deviennent enfin exécutables en groupe. Des plafonds par hôte limitent les dommages ; des poids optionnels par hôte peuvent en outre refléter des politiques de courtoisie.

    Interpréter les résultats des proxies sans pensée magique

    Si la sortie directe échoue tandis que la sortie via un proxy réussit avec des paramètres de groupe identiques, la destination applique probablement une vérification basée sur l’identité réseau. C’est une information opérationnelle utile. Ce n’est cependant pas la preuve que les mécanismes de déclenchement ont changé. Derrière les proxies, vous devez toujours enregistrer les accès par identité de sortie et par hôte cible. Sinon, vous ne faites que déplacer le point aveugle.

    Les politiques de conformité et relatives aux robots restent à votre charge pour être respectées. Un débit global plus élevé grâce à de nombreuses sorties augmente l’impact d’une erreur logique. La régulation progressive via des indicateurs spécifiques et des plafonds par hôte reste possible même avec l’activation de proxies, vous permettant ainsi d’atténuer l’intensité des opérations sans devoir réimplémenter l’infrastructure d’identification.

    Fermer le cycle de l’expérience vers les paramètres par défaut

    Lorsque les mesures montrent que le taux de démarrage et les pics par hôte prédisent mieux les bannissements que la taille totale du pool, il convient d’encodifier ces résultats en paramètres par défaut dans la bibliothèque client : exiger un paramètre RPS (ou min-gap), imposer un plafond par hôte pour les modes multi-hôte, et exporter des métriques pour les deux cas. La documentation doit présenter à côté du tableau de bord correct celui qui montre des valeurs négatives (demandes terminées en hausse tandis que les demandes valides baissent). En expliquant les modes de défaillance, on évite que l’équipe suivante ne « résolve » le problème de concurrence en provoquant une interruption silencieuse des données utiles.

    Réaliser l’expérience de taux de démarrage de manière fiable

    Fixez les versions de Pin Node et d’undici (ou de votre stack HTTP) afin que le comportement du pooling des connexions reste comparable. Désactivez les mécanismes de tentative répétée similaires à ceux des navigateurs qui ne sont pas liés au test pendant les essais. Vider les caches DNS entre les exécutions directes et celles via un proxy si votre outil résout les noms de hôtes différemment. Enregistrez l’heure exacte pour toute la série d’essais, ainsi que les timestamps de début et de fin pour chaque requête, afin de pouvoir visualiser le nombre d’admissions dans les premières demi-seconde — la fenêtre pendant laquelle les clients du pool semblent identiques, quel que soit le niveau de concurrence en état stationnaire que vous pensez avoir configuré.

    Lors de la création des graphiques, affichez toujours le nombre de requêtes réussies à côté du nombre total de requêtes traitées. Un graphique à deux axes qui cache ce chiffre est la cause des métriques trompeuses. Exportez un fichier CSV depuis l’outil afin que d’autres puissent recalculer les résultats sans avoir recours à des captures d’écran.

    Interpréter la rigueur du style Arch Wiki

    Les hébergeurs de documentation publique varient : certains limitent le trafic en fonction de l’IP et du chemin, d’autres en fonction du User-Agent, d’autres encore en fonction des connexions simultanées, ou encore selon le taux de requêtes sur des périodes définies. Un code 429 aujourd’hui peut se transformer en une ralentissement moins marqué demain en raison d’un changement dans la politique de l’hébergeur. C’est pourquoi les chiffres présentés dans cet article ne sont que des exemples illustratifs, et non des constantes éternelles. Ce qui reste inchangé, c’est la méthodologie : définir séparément la taille du pool, le taux d’admission et les pics par hébergeur, puis modifier une variable à la fois.

    Si vous utilisez votre propre hébergeur strict, conservez un hébergeur de contrôle plus souple dans la même expérience. Lorsque le hébergeur de contrôle échoue, votre client est endommagé. Lorsque seul l’hébergeur strict échoue, vous étudiez alors l’interaction de sa politique avec votre plan d’exécution.

    Détails de mise en œuvre du plafond de taux de démarrage

    Un écart minimal dérivé du max RPS est simple et efficace pour les clients à processus unique. Les seaux de tokens permettent des pics courts tout en imposant des moyennes à long terme — utile lorsque l’on souhaite des requêtes interactives rapides mais aussi des balayages en masse plus mesurés. Les seaux fuyants atténuent ces pics davantage. Quel que soit l’algorithme choisi, appliquez-le aux départs, y compris aux tentatives de réessai. Une tempête de réessais qui contourne ces restrictions recrée la situation de foule à t=0 après le premier bannissement.

    Les balayeurs multi-processus ont besoin d’un verrou de validation distribué (Redis, etcd ou un planificateur central). Seuls les écarts locaux ne permettront pas de coordonner cinq travailleurs qui pensent chacun pouvoir lancer deux requêtes par seconde.

    Détails de mise en œuvre des limites par hôte

    Le schéma habituel consiste en des sématiques imbriquées identifiées par l’adresse du hôte : acquérir le sématique global, puis celui du hôte, ensuite récupérer les données, et enfin libérer les sématiques dans l’ordre inverse. Déterminez si www et apex partagent la même clé. Définissez comment les redirections qui modifient le nombre de hôtes s’ajustent aux limites fixées. Les fragments basés sur le chemin d’un même site partagent généralement toujours un budget d’hôtes, sauf si des preuves explicites provenant d’un CDN indiquent le contraire.

    Emettez des métriques : inflight_global, inflight_per_host{host}, admissions_per_second, http_429_total{host}. Déclenchez des alertes lorsque les taux de 429 augmentent, et non seulement lorsque les budgets d’erreurs liés aux codes 5xx sont épuisés.

    Ordre des files d’attente dans les planificateurs en production

    L’ordre du plan du site, la recherche en profondeur à partir d’un point de départ, les files d’attente par priorité pour les URLs « importantes » et les files de tentative permettent toutes de remodeler les pics par hôte. Une file de tentative qui place en tête les URLs d’Arch échouées peut accidentellement récréer l’ordre des blocs après une panne partielle. Une mise en file équitable entre les hôtes — des files prêtes au tour par tour par hôte — réduit la concentration accidentelle même avant l’application de plafonds stricts. Les plafonds stricts restent nécessaires lorsque la seule équité ne suffit pas à limiter les pics en cas de latence inégale.

    Proxies sans auto-illusion

    Les réseaux résidentiels modifient la distribution des identités. Ils ne contreviennent pas à la physique : si chaque identité déclenche toujours cinquante tentatives en même temps, les destinations qui se basent sur le comportement plutôt que sur l’IP peuvent encore bannir. Enregistrez les accès par identité de sortie. Rotuez poliment. Respectez les directives robots et les conditions contractuelles. Préférez un rythme modéré même lorsque les proxies sont activés, afin qu’aucun bug ne puisse se propager sur une large zone.

    Les proxies de datacenter sont moins chers et plus faciles à identifier ; ceux destinés aux utilisateurs résidentiels sont plus coûteux et soulevent des questions éthiques. Choisissez en connaissance de cause ; ne considérez pas le terme « proxy » comme un synonyme de solution définitive.

    De l’expérience à les paramètres par défaut de la bibliothèque

    Intégrez deux paramètres essentiels dans les enveloppes de clients HTTP partagés utilisées par les crawlers : maxInFlight et maxStartsPerSecond, ainsi que maxInFlightPerHost lorsque plusieurs hôtes sont concernés. Refusez de créer un client pour le mode multi-hôte sans cette limite par hôte. Fournissez des modèles de tableau de bord affichant le nombre d’opérations réussies par seconde. Formez les utilisateurs en leur montrant ces deux graphiques : celui de la concurrence montre un débit apparemment élevé mais entraîne la perte de pages utiles.

    Liste de contrôle élargie

    • Imposer des limites aux départs, et non seulement aux slots en cours d’exécution.
    • Imposer des limites par hôte au sein du pool global.
    • Mesurer le nombre d’opérations acceptées et les pics par hôte à chaque exécution.
  • Évaluer le succès en fonction des pages utiles, et non du nombre de connexions terminées.
  • Appliquer un contrôle d’accès aux tentatives de réessai.
  • Utiliser un hôte de contrôle plus souple dans les tests mixtes.
  • Considérer le succès du proxy comme un changement de réputation, et non comme une preuve que l’organisation des tâches est saine.
  • Revoir les chiffres à mesure que les politiques évoluent ; conserver la méthodologie.
  • Jusqu’à ce que ces habitudes s’ancrent, les équipes continueront à « corriger la concurrence » en ciblant des modes de défaillance moins visibles, qui génèrent toujours des erreurs HTTP 429 et consomment encore des budgets de balayage.