Warum das Erhöhen der Konkurrenz zu HTTP 429-Fehlern führt: Startrate und Limits pro Host
Das Erhöhen der Anzahl der Arbeiter ohne Festlegung einer Startrate sowie von Obergrenzen pro Host führt zu Spitzenbelastungen, die 429-Fehler auslösen. Die Durchsatzleistung entsteht durch kontrollierte Parallelisierung, Wartezeiten und ein gut konzipiertes Warteschlangensystem.
Zusammenfassung: Die Steigerung der Konkurrenz wird als kostenlose Parallelisierung vermarktet: mehr Worker, mehr Anfragen, mehr Daten. Zehn erscheint gut, daher müssen zwanzig besser sein und fünfzig unverzichtbar. Das Problem ist, dass die Erhöhung der Pool-Größe nicht nur die Konkurrenz erhöht. Sie bestimmt auch, wie stark der Client vom Host belastet wird – und bei Mehr-Host-Arbeitslasten, welche pro Host bestehende Konkurrenz ohne zusätzliche Einstellungen entsteht. Wenn diese versteckten Faktoren versagen, ist der Statuscode oft HTTP 429, was genauso aussieht wie „zu viele gleichzeitige Anfragen“. Dann verkleinern die Teams den Pool oder fügen Proxy-Server hinzu und machen weiter. Ohne die eigentliche Ursache zu identifizieren, gehen sie wertvollen Durchsatz verloren.
Teil I – Konkurrenz gegen Startrate: Eine Pool-Einstellung steuert zwei Grenzen
Die Größe der Arbeitspool-Gruppe (p-limit(n), ein Semaphor, die Anzahl der Threads) begrenzt, wie viele Operationen gleichzeitig ausgeführt werden können. Sie beschränkt nicht, wie schnell neue Aufgaben beginnen können. Bei t=0 kann ein Pool mit fünfzig wartenden Aufgaben in einem Zeitintervall fünfzig Anfragen starten – es kommt zu einem Anstieg der Startrate – obwohl die Konkurrenz im Gleichgewichtszustand später „nur fünfzig“ erscheint. Rate-Limiter berücksichtigen diesen initialen Anstieg genauso wie den anhaltenden Parallelismus.
Messung des Startrate-Anstiegs
Eine reproduzierbare Testumgebung hilft dabei. Nehmen Sie eine kleine Sammlung von URLs zu Artikeln auf der Arch Linux Wiki:
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
Bauen Sie eine Last von 100 Anfragen auf, indem Sie die Liste durchlaufen:
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)))
);
// ...
}
Führen Sie Tests mit unterschiedlichen Poolsizes durch, klassifizieren Sie jedes Ergebnis als erfolgreich oder als 429 (sowie andere Fehler), und erfassen Sie die pro Sekunde abgeschlossenen Anfragen zusammen mit der Anzahl der erfolgreichen Anfragen. Der Unterschied ist wichtig: Ein schnelles 429 trug zwar zu den sogenannten „abgeschlossenen Anfragen pro Sekunde“ bei, lieferte aber keine Seite.
Nutzlicher Durchsatz nimmt mit steigender globaler Konkurrenz ab
Bei einer festen Arbeitslast von 100 Anfragen, bei der nur die Größe des Pools variiert, verhält sich eine direkte Verbindung sehr schlecht. Bei einer Konkurrenz von 1 sind im Wesentlichen alle 100 Anfragen erfolgreich, und die Anzahl der abgeschlossenen Anfragen pro Sekunde liegt bei etwa 2,4/s – man muss auf die vollständige Verarbeitung warten, ohne dass parallele Anfragen gestartet werden können. Bei einer Konkurrenz von 10 sinkt die Anzahl der erfolgreichen Anfragen stark: etwa 42 Seiten werden erfolgreich verarbeitet, während 58% der Anfragen mit 429 fehlschlagen; die Anzahl der abgeschlossenen Anfragen pro Sekunde steigt auf etwa 23,5/s. Bei Konkurrenzzahlen von 25 und 50 sinkt die Anzahl der erfolgreichen Anfragen weiter (etwa 24/100, dann 16/100), während die Anzahl der abgeschlossenen Anfragen pro Sekunde weiterhin in den einstelligen oder zwanzigern liegt (~18,2/s und 21,8/s).
Der Höhepunkt der Belastung liegt bei einer Konkurrenz von 10 mit 23,5 abgeschlossenen Anfragen pro Sekunde – dem höchsten Wert in der Tabelle – verbunden mit einem der schlechtesten Werte bei den erfolgreichen Anfragen. Eine Optimierung nach Anzahl der pro Sekunde abgeschlossenen Anfragen würde eine Einstellung empfehlen, die den größten Teil des Budgets für Sperrungen verbraucht. Der nützliche Durchsatz ist erfolgreiche Anfragen pro Sekunde, nicht die Gesamtzahl der abgeschlossenen Anfragen.
Durch Setzen einer Obergrenze für die Startrate bei derselben Konkurrenz lässt sich das Problem beheben
Lassen Sie die Größe des Pools unverändert und legen Sie fest, wie bald die nächste Anfrage starten darf. Bestimmen Sie einen Mindestabstand anhand einer Obergrenze für die Anfragen pro Sekunde:
// 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;
}
Durch gezielte Steuerung kann bei derselben Konkurrenz, die zuvor den Host überlastet hat, fast alle Seiten bearbeitet werden, da der plötzliche Anstieg an Anfragen verschwindet. Das richtige Denkmodell umfasst zwei Einstellungen: Konkurrenz (aktuell laufende Anfragen) und Startrate (Anzahl der zulässigen Anfragen pro Sekunde). Wenn man diese in eine einzige Zahl zusammenfasst, wird das Versagensmuster verdeckt.
Warum ein gemeinsam genutzter HTTP-Client bei t=0 eine Anzahl von Anfragen sendet
Pools kennen kein rücksichtsvolles Tempo. Sie kennen nur freie Slots. Beim Start sind alle Slots frei, daher werden alle in der Warteschlange stehenden Aufgaben ausgeführt. Die Wiederverwendung von Verbindungen sowie das HTTP/2-Multiplexing können dazu führen, dass diese Anfragenmenge noch intensiver übertragen wird. Nur ein Zulassungskontroller – wie Token-Bucket, Min-Gap oder Leaky Bucket – kann den Start unabhängig von Kapazitätsbeschränkungen steuern.
Bessern Wohnproxys die Anfangsrate der Anfragen?
Rücksichtsvolles Vorgehen ist die Lösung für Korrektheitsschwierigkeiten: Daten sammeln, ohne den Zielserver zu überlasten. In der Praxis erfordern manche Anforderungen jedoch eine Geschwindigkeit, die eine Beschränkung von 2,4 Anfragen pro Sekunde nicht erreichen kann. Derselbe naive Ansatz – ohne Begrenzung der Anfangsrate, mit plötzlichem Auslösen aller Aufgaben bei t=0 – über Wohnproxys kann bei nahezu allen Konkurrenzniveaus etwa 98–99/100 Erfolge erzielen, ohne dass es zu Sperrungen kommt, während der direkte Weg weiterhin zu Problemen führt.
Das bedeutet nicht, dass Proxys den Bedarf an einem Verständnis der Startrate beseitigen. Sie verändern das IP-Rufverhalten sowie die Art und Weise, wie eine Website den Traffic zuordnet. Sie können einen plötzlichen Anstieg verbergen, der sonst eine einzelne Ausgangs-IP blockieren würde. Wenn die Produktanforderung darin besteht, „Daten zu sammeln, ohne jemals wie eine Stampede auszusehen“, bleibt das Pacing die primäre Steuerungsmethode; Proxys sind lediglich eine Kapazitäts- und Rufverhaltensschicht, kein Ersatz für die Messung der Zugriffe.
Die Konfiguration von Proxy-Agenten zieht in der Regel die Anmeldeinformationen aus der Umgebung ab:
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);
Verwenden Sie sie bewusst, protokollieren Sie, ob eine Aktion direkt oder über einen Proxy durchgeführt wurde, und erfassen Sie weiterhin die beobachtete Start-RPS-Zahl, damit Sie wissen, welche Methode das Ergebnis beeinflusst hat.
Teil II – Konkurrenz pro Host
Globale Pools verbergen außerdem eine weitere, neu entstehende Beschränkung. Betrachten Sie Folgendes:
const limit = pLimit(50);
await Promise.all(urls.map((url) => limit(() => fetchOne(url))));
Fünfzig globale Slots, die auf mehrere Hosts verteilt sind, bedeuten nicht, dass jeder Host maximal fünfzig Anfragen gleichzeitig verarbeiten kann. Die Reihenfolge in der Warteschlange sowie Abweichungen im Antwortzeitverhalten bestimmen, wie viele Anfragen ein bestimmter Host tatsächlich bearbeitet. Eine gemischte Liste mag auf dem Papier ausgewogen erscheinen:
1. arch
2. github
3. arch
4. mdn
5. npm
6. arch
7. cloudflare
doch sie kann den strengen Host dennoch mit einem Ansturm konfrontieren, wenn dessen URLs sich im ausführbaren Set befinden.
Benchmarking der Konkurrenzfähigkeit pro Host
Nutzen Sie das Testframework mit zwei Hosts:
- Ein strenger Host – die Arch Linux Wiki (10 Artikel-URLs), der unter Belastung blockiert, wie in Teil I beschrieben.
- Ein nachsichtiger Host – die Katalogseiten von
books.toscrape.com(10 URLs), die selten blockieren und als Kontrollgruppe dienen. Fällt das Sandbox-Umfeld aus, ist der Client fehlerhaft.
Eine abwechselnde Liste ergibt 20 URLs × 5 Wiederholungen = 100 Anfragen bei einer globalen Konkurrenz von 50. Liste der Fixtures sowie die zugehörigen Builder befinden sich im Repository open-concurrency-trap-bench (zum Beispiel 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
Geordnete Arbeitslasten können im Round-Robin-Modus arbeiten, nach Host blockieren oder mithilfe eines Seeds neu geordnet werden:
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;
}
Die Spitzenbelastung pro Host wird anhand der Start-/Ende-Events gemessen:
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
}
Die globale Konkurrenz bleibt konstant; nur die Reihenfolge ändert sich. Dadurch wird der Druck pro Host von der Größe des Pools getrennt.
Wie die Warteschlangenreihenfolge die Konkurrenz pro Host verändert
Echte Crawler befolgen niemals für immer ein perfektes Round-Robin-Verfahren. Sitemaps, Abhängigkeitsgraphen sowie Wiederholungs-Warteschlangen ordnen die Aufgaben neu. Daher können identische globale Einstellungen unterschiedliche Spitzenwerte pro Host erzeugen.
1. Round-Robin: gleichmäßige Abwechslung der Hosts
Die abwechselnde Liste (A B A B …) verteilt die Arbeit. Der Spitzenwert der laufenden Anfragen am strengen Host bleibt relativ gering, da der andere Host ständig Slots beansprucht.
2. Block: Gruppierung von Anfragen am selben Host
Durch die Gruppierung aller Arch-URLs sowie aller Buch-URLs erhält der strenge Host eine lange Abfolge von ausführbaren Anfragen. Der Spitzenwert der laufenden Anfragen an diesem Host steigt trotz einer „Konkurrenzrate von weiterhin 50“ an, je nach Größe des globalen Pools.
3. Shuffling (Seed 42): zufällige Reihenfolge
Ein mit einem Seed gesteuertes Shuffling liegt zwischen den Extremen und spiegelt die zufällige Reihenfolge in der Produktion wider. Die Spitzenwerte ändern sich mit dem Seed; die Lehre daraus ist die Empfindlichkeit gegenüber solchen Faktoren, nicht eine magische Permutation.
Die gleiche globale Konkurrenzrate, unterschiedlicher Druck pro Host
Durch diese Muster erreicht die Erfolgsrate beim strengen Host in der Flugphase seinen gemessenen Höchstwert enger als bei dem konstanten globalen Wert c=50. Der nachsichtige Host bleibt weiterhin funktionsfähig. Die Reduzierung des globalen Pools, um den strengen Host „zu beheben“, würde auch den nachsichtigen Host beeinträchtigen und den Höchstwert des strengen Hosts bei ungünstiger Reihenfolge weiterhin nicht begrenzen.
Lösung: Konkurrenz pro Host getrennt begrenzen
In Teil I wurde neben dem Pool eine Obergrenze für die Startrate hinzugefügt. In Teil II wird zusätzlich eine Obergrenze pro Host neben dem Pool eingeführt: eine zusätzliche Beschränkung dafür, wie viele laufende Anfragen ein einzelner Hostnamen haben darf.
Erinnern wir uns an die drei Begriffe:
- Globaler Konkurrenzgrad – Größe des gemeinsamen Pools (zum Beispiel
p-limit(50)). Diesen legt man fest. - Konkurrenzgrad pro Host – Was ein einzelner Host tatsächlich erlebt (
peak_inflight). Diesen misst man.
Fehlt diese auf Host-Ebene festgelegte Beschränkung, können alle freien globalen Slots von den URLs in Anspruch genommen werden, die gerade verfügbar sind – was zu einer Überlastung eines bestimmten Hosts führen kann. Durch die hinzugefügte untergeordnete Grenze erhält jeder Hostnamen seine eigene Warteschlangenkontrolle:
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();
});
}
// ...
}
Nun kann eine Gruppe von URLs mit strengen Einschränkungen nicht alle globalen Slots gleichzeitig belegen. Der weniger restriktive Host kann weiterhin die verbleibende Kapazität nutzen. Die Sperrungen stehen in Zusammenhang mit der begrenzten Variable, die Sie kontrollieren wollten.
Warum ein gemeinsamer Worker-Pool die Last auf einen Host konzentriert
Der Pool füllt die Slots mit allem, was ausgeführt werden kann. Schnelle Hosts befreien die Slots schnell und übernehmen mehr eigene Aufgaben – bis eine Gruppe von Hosts mit strengen Einstellungen gemeinsam ausgeführt werden kann und einen Burst erbt. Die Größe des Bursts hängt von der Reihenfolge, der Latenz und dem Mischverhältnis ab – Faktoren, die nicht im einzigen Konkurrenz-Integer enthalten sind. Deshalb ist ein pro-Host-Limiter besser als ein blindes Verkleinern des globalen Pools.
Eine praktische Checkliste
- Betrachten Sie die Startrate als eigene Einstellung. Ein Semaphor ist kein RPS-Limiter. Fügen Sie neben der Konkurrenzrate eine explizite Obergrenze für die Startrate hinzu.
- Betrachten Sie die Konkurrenz pro Host als eigene Einstellung. Mehr-Host-Arbeitslasten benötigen einen pro-Host-Limiter innerhalb des globalen Pools.
- Protokollieren Sie
observed_start_rpssowie die pro-Host-Werte vonpeak_inflight. Man kann keine Grenzen bewerten, die man nie gemessen hat.
Genaue Schwellenwerte bei einem einzelnen Blog-Run sind nicht übertragbar: Die Limitierungen hängen vom Zustand ab und berücksichtigen Zeit, Verkehrsgeschichte sowie die IP-Reputation. Das übertragbare Muster ist Isolation. Ein Fehler, der wie „zu viel Konkurrenz“ aussieht, kann ein Problem mit der Startrate sein, ein Scheduling-Problem pro Host oder beides zugleich. Solange diese Variablen nicht getrennt werden, beseitigt das Verkleinern des Pools nur ein Symptom – und oft das falsche.
Die Metriken lesen, ohne sich selbst zu täuschen
Die Anzahl der abgeschlossenen Anfragen pro Sekunde steigt, sobald der Client Sockets schnell öffnen kann – auch dann, wenn die meisten Antworten Ablehnungen sind. Dashboards, die Konkurrenzsituationen ohne OK-Filter auswerten, empfehlen oft fehlerhafte Einstellungen. Verknüpfen Sie jeden Durchsatzgraphen mit einem OK-Verhältnis und ggf. auch mit der Menge an gespeichertem nützlichem Inhalt in Bytes. Wenn Ihr Pipeline-System 429-Fehler erneut versucht, zählen Sie diese Versuche getrennt, damit eine Flut an Wiederholungsversuchen nicht wie produktiver Parallelismus erscheint.
Startrate-Protokolle sollten das Zeitstempel jeder Zulassung erfassen, nicht nur der jeweiligen Abschluss. Anhand dieser Zulassungen können Sie Spitzenwerte in den ersten 100–500 ms rekonstruieren – in diesem Zeitraum sehen viele gemeinsam genutzte Clients unabhängig von der konfigurierten Größenordnung des Pools identisch aus. Wenn zwei Konfigurationen denselben Startspitzenwert aufweisen, sollten Sie nicht überrascht sein, wenn sie auch dasselbe Sperrmuster zeigen.
Proxy-Server, Reputation und Ehrlichkeit bezüglich von Kompromissen
Wohnungs- oder Rechenzentrums-Proxy-Server verteilen Identitäten neu. Sie können eine Angriffswelle mit einer einzigen IP-Adresse in viele ruhigere Datenströme umwandeln. Das unterstützt Sammelprojekte und kann ein schwaches Zutrittskontrollsystem vor einem Ziel verbergen, das Limits für IP-Adressen vorgibt. Es beseitigt weder die ethischen noch die vertraglichen Pflichten gegenüber den von Ihnen abgerufenen Seiten noch das technische Bedürfnis, Ihren eigenen Client zu verstehen. Ziehen Sie zunächst eine kontrollierte Geschwindigkeit vor, wenn Sie den Client steuern können; verwenden Sie Proxy-Server, wenn das Produkt tatsächlich eine höhere Gesamtdurchsatzrate über viele Identitäten erfordert – und messen Sie weiterhin den Druck pro Identität und pro Host, damit Sie nicht blind hinter der Proxy-Schicht vorgehen.
Zusammenführung beider Aspekte
Produktions-Crawlers benötigen in der Regel sowohl eine globale Pufferung zur Sicherstellung der Ressourcenverfügbarkeit auf Ihrer Seite, als auch eine Obergrenze für die Startrate zur Wahrung der Höflichkeit beim Zugriff, sowie Host-spezifische Obergrenzen, wenn mehrere Ziele denselben Puffer teilen. Das Auslassen eines dieser Elemente führt zu einem Fehlermuster, das in HTTP-Statuscodes wie Konkurrenz erscheint. Die Lösung ist nicht mystisch – es handelt sich um Instrumentierung sowie einen zweiten Limitierer, der auf den tatsächlich beobachteten Faktor ausgerichtet ist.
Nutzung von Designhinweisen, um faire Vergleiche zu gewährleisten
Sicherstellen Sie, dass die Taxonomie der Erfolge bei allen Durchläufen identisch bleibt: OK, HTML, HTTP 429, andere 4xx/5xx-Fehler, Zeitüberschreitungen sowie Parsing-Fehler sollten bei jedem Lauf auf dieselbe Weise gekennzeichnet werden. Ändern Sie nur die getestete Variable – Größe des Pools, Mindestanfangsabstand, Aktivierung/Deaktivierung eines Proxys oder Reihenfolge der Warteschlange. Nutzen Sie bei Möglichkeit bereits vorgewärmte DNS- und TLS-Einstellungen, damit die ersten Messpunkte einer Kurve nicht durch Störungen beim ersten Verbinden beeinflusst werden, es sei denn, ein kalter Start ist ausdrücklich Teil der Untersuchung.
Wiederholen Sie die Arbeitslasten so oft wie nötig, um Störungen zu reduzieren, aber nicht so oft, dass die adaptive Begrenzung eines Servers mitten im Experiment dauerhaft angepasst wird. Falls die Begrenzungsmechanismen Zustandsabhängigkeiten aufweisen, notieren Sie die Tageszeit sowie ob vorherige Sperrungen noch wirken könnten. Veröffentlichen Sie die Startwerte für das Shuffling, damit andere die Reihenfolgeffekte nachvollziehen können.
URL-Fixierungen sollten stabil sein. Wiki-Überschriften sowie Katalogseiten im Sandbox-Bereich, die während der Analyse verschwinden, verfälschen die OK-Zahlen. Führen Sie wie im Projekt concurrency-trap-bench Listen für Pin-Objekte im Versionskontrollsystem neben dem Harness, damit die Diagramme auf bekannte Eingaben verweisen können.
Wie „nützliche Durchsatzraten“ in einem Pipeline-System aussehen
Downstream-Systeme kümmern sich um akzeptierte Dokumente und nicht um Socket-Aktivitäten. Wenn ein Scraper einen Indexierer versorgt, zählen Sie die pro Minute indexierten Dokumente. Wenn er eine Preisdatenbank versorgt, zählen Sie die validierten Zeilen. Passen Sie das Optimierungsziel an diese Geschäftseinheit an. Andernfalls wird die Ingenieurabteilung eine Proxy-Metrik – abgeschlossene HTTP-Austausche – maximieren, die viele 429-Fehler enthalten.
Wiederholte Anfragen wirken sich negativ auf Pools ohne Rhythmussteuerung aus. Ein plötzlicher Anstieg, der zu Sperrungen führt, gefolgt von sofortigen Wiederholungsversuchen, kann die Startrate weiter erhöhen. Verwenden Sie bei 429-Fehlern Jitter, beachten Sie das Retry-After-Attribut, falls vorhanden, und lassen Sie auf keinen Fall, dass Wiederholungsstürme die Startrate überschreiten. Der Zulassungskontroller muss Wiederholungsanfragen als neue Anfragen betrachten.
Mehrfachnutzer- und Mehrhost-Scheduler
Dienste, die im Auftrag vieler Kunden arbeiten, verfügen oft bereits über globale Konkurrenzgrenzen zur Gewährleistung der Prozesssicherheit. Dennoch benötigen sie Budgets pro Ziel, damit die URL-Liste eines Kunden keinen empfindlichen Server monopolisieren kann. Verschachtelte Grenzen – global, pro Mehrfachnutzer, pro Host – ergänzen sich gegenseitig. Implementieren Sie sie als explizite Ebenen, anstatt zu erwarten, dass eine faire Warteschlange aus einem einzigen Semaphor entsteht.
Wenn die Latenzzeiten der Hosts um Größenordnungen variieren, neigen Work-Stealing-Pools dazu, sich auf den schnellen Host auszurichten. Diese Neigung ist vorteilhaft für die Auslastung, aber nachteilig für den langsamen Host, sobald seine URLs endlich in einer Gruppe ausgeführt werden können. Pro-Host-Obergrenzen begrenzen den Schaden; optionale pro-Host-Gewichte können weitere Höflichkeitsrichtlinien widerspiegeln.
Interpretation von Proxy-Ergebnissen ohne magisches Denken
Falls ein direkter Ausgang fehlschlägt, während ein über Proxy abgewickelter Ausgang bei identischen Pool-Einstellungen erfolgreich ist, basiert die Zieladresse vermutlich auf der Überwachung der Netzwerkidentität. Das ist nützliches Betriebswissen – es beweist jedoch nicht, dass sich die Startrate-Physik geändert hat. Hinter Proxys sollten Sie weiterhin Zugriffe nach Identität des Ausgangspunkts und nach Zielhost protokollieren. Andernfalls verschieben Sie lediglich den Blindpunkt.
Die Richtlinien zur Konformität und zu Robotern bleiben weiterhin Ihre Pflicht zum Einhalten. Eine höhere Gesamtleistung durch viele Ausgänge erhöht den Einflussbereich eines Logikfehlers. Durch das Steuern der Funktionsaktivierung sowie Obergrenzen pro Host – auch bei aktivierten Proxys – können Sie die Aggressivität verringern, ohne die Identitätsinfrastruktur neu bereitzustellen.
Vom Experiment zu den Standardeinstellungen
Sobald Messungen zeigen, dass die Startrate sowie die Peaks pro Host bessere Vorhersagen für Sperrungen liefern als allein die Gesamtanzahl der Verbindungen, sollten diese Erkenntnisse als Standardeinstellungen in der Client-Bibliothek kodiert werden: Es sollten ein RPS- oder Min-Intervall-Parameter erforderlich sein, sowie Obergrenzen pro Host für Mehr-Host-Modi, wobei Metriken für beide Fälle exportiert werden müssen. Die Dokumentation sollte neben der guten Darstellung auch die schlechte Anzeige zeigen (steigende abgeschlossene Anfragen pro Sekunde bei sinkenden gültigen Anfragen). Das Verständnis der Fehlermöglichkeiten verhindert, dass das nächste Team die Konkurrenzfähigkeit versehentlich zu einem stillen Ausfall nützlicher Daten führt.
Saubere Durchführung des Start-Rate-Experiments
Stabilisieren Sie die Versionen von Pin Node und undici (oder Ihrer HTTP-Stack-Lösung), damit sich das Verhalten des Connection-Pooling vergleichen lässt. Deaktivieren Sie während der Tests uner relevante, browserähnliche Retry-Middleware-Funktionen. Leeren Sie die DNS-Caches zwischen direkten und über Proxy durchgeführten Tests, falls Ihr Tool Hosts unterschiedlich auflöst. Erfassen Sie die Gesamtlaufzeit der gesamten Testreihe sowie die Start- und Endzeiten pro Anfrage, damit Sie die Anzahl der Anfragen in den ersten halben Sekunde darstellen können – dem Zeitraum, in dem die gepoolten Clients unabhängig von der von Ihnen angenommenen Konkurrenzlast identisch erscheinen.
Beim Erstellen von Diagrammen sollten Sie stets die Anzahl der erfolgreichen Anfragen neben der Anzahl der abgeschlossenen Anfragen angeben. Ein Diagramm mit zwei Achsen, das die Anzahl der erfolgreichen Anfragen versteckt, führt dazu, dass sinnlose Metriken als aussagekräftig angesehen werden. Exportieren Sie CSV-Dateien aus dem Testtool, damit andere die Ergebnisse ohne Verlass auf Screenshots neu berechnen können.
Interpretation der Strenge im Arch-Wiki-Stil
Die Hosts für öffentliche Dokumentationen variieren: Einige begrenzen die Nutzung nach IP-Adresse und Pfad, andere nach User-Agent, wieder andere nach gleichzeitigen Verbindungen oder nach der Anfragenrate innerhalb bestimmter Zeitfenster. Was heute als 429-Fehler auftreten kann, wird morgen aufgrund von Änderungen in der Richtlinie des Anbieters möglicherweise weniger ausgeprägt sein. Deshalb dienen die in dem Artikel genannten Zahlen nur als veranschaulichende Muster und nicht als ewige Konstanten. Entscheidend ist vielmehr die Methodik: Man teilt die Größe des Pools, die Zulassungsrate sowie die Spitzenwerte pro Host voneinander ab und ändert anschließend jeweils nur eine Variable nach der anderen.
Falls Sie Ihren eigenen strengen Host verwenden, halten Sie im selben Testlauf auch einen nachsichtigeren Kontrollhost bereit. Fällt der Kontrollhost aus, ist Ihr Client beschädigt. Wenn nur der strenge Host ausfällt, können Sie beobachten, wie deren Richtlinien mit Ihrem Zeitplan interagieren.
Detaillierungen zur Umsetzung der Startrate-Begrenzung
Ein aus der maximalen Anzahl an Anfragen pro Sekunde abgeleiteter Mindestabstand ist für Ein-Prozess-Kunden einfach und effektiv. Token-Behälter ermöglichen kurze Anfragswellen, während gleichzeitig langfristige Durchschnittswerte gewährleistet werden – nützlich, wenn man schnelle interaktive Abfragen möchte, aber gleichzeitig eine behutsame Massenabfrage. Undichte Behälter glätten die Schwankungen stärker. Welchen Algorithmus Sie auch wählen, wenden Sie ihn auf die Startvorgänge, einschließlich der Wiederholungsversuche, an. Eine Wiederholungssturm, der die Beschränkungen umgeht, erzeugt nach dem ersten Verbot erneut das Chaos wie zu Beginn (t=0).
Mehrprozess-Scrapers benötigen einen verteilten Zugriffsschutz (Redis, etcd oder einen zentralen Scheduler). Allein lokale Abstände reichen nicht aus, um fünf Worker zu koordinieren, die jeweils glauben, zwei Anfragen pro Sekunde starten zu dürfen.
Detaillierungen zur Umsetzung der Host-basierten Beschränkung
Die übliche Vorgehensweise bei semaphorenartigen Strukturen, die nach Hostnamen sortiert sind, besteht darin: Zuerst wird das globale Semaphor erworben, anschließend das des Hosts, danach wird der gewünschte Inhalt abgerufen und schließlich im umgekehrten Verlauf wieder freigegeben. Entscheiden Sie, ob www und apex denselben Schlüssel teilen. Bestimmen Sie außerdem, wie Umleitungen, die die Anzahl der Hosts ändern, mit den festgelegten Obergrenzen umgegangen werden sollen. Pfadbasierte Teile derselben Website teilen in der Regel weiterhin denselben Host-Budget, es sei denn, es liegen ausdrückliche Hinweise auf ein CDN vor.
Emitieren Sie Metriken wie inflight_global, inflight_per_host{host}, admissions_per_second sowie http_429_total{host}. Senden Sie Alarme, wenn die 429-Fehlerraten ansteigen, und nicht nur dann, wenn die Fehlerbudgets für 5xx-Fehler aufgebraucht sind.
Warteschlangenreihenfolge in Produktions-Schedulern
Die Reihenfolge der Sitemap, BFS ab einer Ausgangspunkt-URL, Prioritätswarten für „wichtige“ URLs sowie Wiederholungsqueues verändern allein schon die Spitzenlast pro Host. Ein Wiederholungsqueue, der fehlgeschlagene Arch-URLs an den Anfang setzt, kann nach einem teilweisen Ausfall versehentlich die Reihenfolge der Blockverarbeitung erneut herstellen. Ein fairer Warteschlangenbetrieb über alle Hosts – mit runden-robin-basierten Warteschlangen pro Host – verringert bereits vor dem Eintritt in feste Obergrenzen eine versehentliche Konzentration der Last. Feste Obergrenzen bleiben jedoch notwendig, wenn allein die Fairness unter ungleichmäßiger Latenz keine Kontrolle über die Spitzenlasten ermöglicht.
Proxys ohne Selbsttäuschung
Wohnnetzwerke verändern die Verteilung der Identitäten. Sie heben jedoch die Physik nicht auf: Wenn jede Identität weiterhin gleichzeitig fünfzig Anfragen sendet, können Ziele, die sich nach Verhalten statt nach IP-Adresse richten, weiterhin Blockaden verhängen. Protokollieren Sie die Zugriffe nach jeder Ausgangsidentität. Wechseln Sie höflich zwischen Identitäten. Befolgen Sie die Richtlinien für Suchmaschinenroboter sowie vertragliche Bedingungen. Wählen Sie selbst bei aktivierten Proxys eine gemäßigte Geschwindigkeit, damit ein Fehler nicht zu einem weitreichenden Problem wird.
Datenzentrum-Proxys sind günstiger und leichter zu identifizieren; Wohnungsproxys sind teurer und ethisch heikler. Wählen Sie sorgfältig – betrachten Sie „Proxy“ nicht als Synonym für eine Lösung.
Von Experimenten zu Standardeinstellungen in Bibliotheken
Fügen Sie zwei notwendige Einstellmöglichkeiten in die von Crawlern verwendeten gemeinsamen HTTP-Client-Wrapper ein: maxInFlight und maxStartsPerSecond, sowie maxInFlightPerHost, wenn mehr als ein Host vorhanden ist. Weigern Sie sich, einen Client für den Mehr-Host-Modus ohne diese pro-Host-Beschränkung zu erstellen. Stellen Sie Dashboard-Vorlagen bereit, die die Anzahl der erfolgreichen Anfragen pro Sekunde darstellen. Schulen Sie die Nutzer mit beiden Diagrammen: Einerseits zeigt er die Konkurrenzfähigkeit bei hohem Durchsatz, andererseits die Verluste an nützlichen Seiten.
Erweiterte Checkliste
- Beschränken Sie nicht nur die aktiven Verbindungen, sondern auch die Anzahl der Starts.
- Beschränken Sie die Anzahl der Verbindungen pro Host innerhalb des globalen Pools.
- Messen Sie bei jedem Lauf die Anzahl der erfolgreich abgewickelten Anfragen sowie die Spitzenwerte pro Host.
Bis diese Gewohnheiten verinnerlicht sind, werden Teams weiterhin versuchen, „Konkurrenzen“ in leisere Fehlermuster umzuwandeln, die weiterhin HTTP 429 zurückgeben und weiterhin Crawling-Budgets verbrauchen.