← Zurück zum Blog

Sellerie, RQ oder BullMQ: So erstellen Sie eine Aufgabenwarteschlange mit Proxy und umgehen die IP-Anfragebeschränkung

Wir analysieren die Architektur von Task-Queues mit Proxy-Rotation in Celery, RQ und BullMQ – wie man die Last zwischen IP-Adressen verteilt und Rate-Limits vermeidet.

📅20. Oktober 2026

Wenn ein Parser oder Bot Hunderte von Anfragen pro Minute von einer IP sendet, aktiviert der Server schnell das Rate-Limit, und dann folgt ein Captcha oder ein Verbot. Die Lösung ist eine Aufgabenwarteschlange, die die Anfragen zwischen Workern und Proxys verteilt. In diesem Artikel werden wir untersuchen, wie eine solche Architektur in Celery, RQ und BullMQ funktioniert und wie man das Anfrage-Limit für jede IP-Adresse effektiv verwaltet.

Warum eine Aufgabenwarteschlange bei der Arbeit mit Proxys

Wenn die Aufgabe darin besteht, 50.000 Produktkarten von Wildberries zu scrapen oder den Status von 10.000 Instagram-Konten zu überprüfen, dauert die sequenzielle Ausführung der Anfragen Stunden, und die parallele Ausführung ohne Kontrolle führt schnell zu einem IP-Verbot. Eine Aufgabenwarteschlange (task queue) löst beide Probleme: Sie zerlegt ein großes Arbeitsvolumen in kleine Aufgaben, verteilt diese auf mehrere Worker und ermöglicht es, jedem Worker einen separaten Proxy zuzuweisen.

Die Architektur sieht normalerweise so aus: Ein Nachrichtenbroker (Redis oder RabbitMQ) speichert die Aufgabenwarteschlange, die Worker holen Aufgaben aus der Warteschlange und führen sie aus, jeder Worker verwendet seinen eigenen Proxy oder rotiert Proxys aus einem Pool. Dies ermöglicht es, die Last auf jede IP innerhalb sicherer Grenzen zu halten, selbst wenn das gesamte Anfragevolumen enorm ist.

Dieser Ansatz wird nicht nur von Entwicklern von Parsern verwendet, sondern auch von Teams, die die Überprüfung von Facebook Ads-Konten automatisieren, Preise auf Ozon überwachen oder Daten von Dutzenden von Websites gleichzeitig sammeln. Ohne eine Warteschlange und eine durchdachte Verteilung nach IP ist jedes solche System zum Scheitern verurteilt, schon am ersten Tag des Betriebs.

Das Problem des Anfrage-Limits für eine IP

Die meisten Websites und APIs haben einen Schutz gegen übermäßige Last. Dies kann sich unterschiedlich äußern: Der Server gibt HTTP 429 (Too Many Requests) zurück, zeigt ein Captcha an, sperrt die IP vorübergehend für 10-60 Minuten oder fügt sie dauerhaft zur schwarzen Liste hinzu. Die Limits variieren stark zwischen verschiedenen Plattformen - zum Beispiel erlaubt die Wildberries API eine begrenzte Anzahl von Anfragen pro Sekunde von einer Adresse, während Instagram bereits nach 15-20 aufeinanderfolgenden Aktionen auf verdächtige Aktivitäten reagiert.

Der größte Fehler beim Aufbau einer Warteschlange besteht darin, alle Worker über dieselbe IP zu starten. Selbst wenn die Aufgaben auf 20 Worker verteilt sind, aber alle denselben Proxy verwenden oder ihn überhaupt nicht verwenden, wird das Limit für diese IP genauso schnell erreicht wie bei sequenzieller Ausführung. Daher sollte die Anzahl der Worker mit der Anzahl der verfügbaren IP-Adressen im Proxy-Pool in Einklang stehen.

Für Aufgaben mit hoher Anfragefrequenz für eine Domain ist es besser, Datacenter-Proxys zu verwenden - sie bieten geringe Latenz und sind für großflächiges paralleles Scraping geeignet, wo Geschwindigkeit wichtiger ist als die Tarnung als normaler Benutzer. Wenn das Ziel jedoch darin besteht, eine strengere Schutzmaßnahme wie Anti-Bot-Systeme von Marktplätzen oder sozialen Netzwerken zu umgehen, sind IPs erforderlich, die normalen Benutzern ähneln, also Wohn- oder mobile IPs.

Celery: Architektur und Proxy-Rotation im Code

Celery ist die reifste und beliebteste Aufgabenwarteschlange im Python-Ökosystem. Sie arbeitet über einem Broker (häufig Redis oder RabbitMQ) und unterstützt eine flexible Konfiguration von Parallelität, Wiederholungsversuchen und Prioritäten von Aufgaben. Für die Proxy-Rotation ist es praktisch, einen IP-Pool in Redis zu speichern und jedem Auftrag beim Einreihen in die Warteschlange einen Proxy zuzuweisen.

from celery import Celery
import requests
import redis
import random

app = Celery('tasks', broker='redis://localhost:6379/0')
redis_client = redis.Redis(host='localhost', port=6379, db=1)

PROXY_POOL = [
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
]

def get_proxy():
    # Round-robin über Redis, um nicht einen IP zu überlasten
    idx = redis_client.incr("proxy_cursor") % len(PROXY_POOL)
    return PROXY_POOL[idx]

@app.task(bind=True, max_retries=3, default_retry_delay=30)
def fetch_url(self, url):
    proxy = get_proxy()
    try:
        response = requests.get(
            url,
            proxies={"http": proxy, "https": proxy},
            timeout=15
        )
        if response.status_code == 429:
            raise self.retry(exc=Exception("Rate limit erreicht"))
        return response.status_code
    except requests.exceptions.RequestException as exc:
        raise self.retry(exc=exc)

Der Schlüsselpunkt ist die Funktion get_proxy(), die über einen Redis-Zähler den nächsten Proxy aus dem Pool im Rundlauf bereitstellt. Dies gewährleistet, dass die Last gleichmäßig auf alle IPs verteilt wird und sich nicht auf einer konzentriert. Der Parameter max_retries und die Behandlung des Codes 429 ermöglichen es der Aufgabe, automatisch mit einem anderen Proxy erneut gesendet zu werden, wenn die aktuelle IP eine Einschränkung erhalten hat.

Zur Kontrolle der Parallelität auf der Ebene der Worker ermöglicht Celery die Begrenzung der Anzahl gleichzeitiger Aufgaben über den Parameter --concurrency beim Starten des Workers sowie durch Warteschlangen mit unterschiedlicher Priorität (z. B. eine separate Warteschlange für langsame Aufgaben mit striktem Rate-Limit).

RQ (Redis Queue): Eine einfache Alternative

RQ ist eine leichtere Bibliothek als Celery, ohne RabbitMQ-Broker und mit minimaler Konfiguration. Sie eignet sich, wenn das Projekt klein ist und keine komplexe Aufgabenrouting benötigt, sondern einfach eine Warteschlange mit Workern und die Möglichkeit, Proxys an Aufgaben zu binden.

from redis import Redis
from rq import Queue
import requests
import itertools

redis_conn = Redis()
q = Queue(connection=redis_conn)

PROXY_POOL = [
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
]
proxy_cycle = itertools.cycle(PROXY_POOL)

def fetch_with_proxy(url):
    proxy = next(proxy_cycle)
    response = requests.get(
        url,
        proxies={"http": proxy, "https": proxy},
        timeout=15
    )
    if response.status_code == 429:
        # Wiederholte Einreihung in die Warteschlange mit Verzögerung
        q.enqueue_in(60, fetch_with_proxy, url)
        return None
    return response.status_code

# Aufgaben in die Warteschlange einreihen
urls = ["https://example.com/page1", "https://example.com/page2"]
for url in urls:
    q.enqueue(fetch_with_proxy, url)

In RQ funktioniert die Rotation über itertools.cycle innerhalb des Worker-Prozesses - das ist einfacher als ein Redis-Zähler, skaliert jedoch schlechter bei vielen parallelen Workern, da jeder Worker den Zyklus von vorne beginnt. Für kleine Projekte mit 1-5 Workern reicht das aus, für größere ist es besser, zu einem zentralisierten Zähler in Redis zurückzukehren, wie im Beispiel mit Celery.

RQ eignet sich gut für Entwickler, die bereits Redis im Projekt verwenden und keinen separaten Nachrichtenbroker einrichten möchten. Der Nachteil ist, dass es im Vergleich zu Celery weniger integrierte Mechanismen zur Priorisierung und Überwachung gibt, weshalb für Produktionssysteme mit Hunderttausenden von Aufgaben pro Tag häufig Celery gewählt wird.

BullMQ: Warteschlange auf Node.js mit IP-Beschränkung

BullMQ ist der Standard für Aufgabenwarteschlangen im Node.js-Ökosystem, basiert auf Redis und hat einen integrierten Rate-Limiter, was besonders praktisch für die Kontrolle des Anfrage-Limits auf IP-Ebene ist. Anstatt die Anfragen manuell zu zählen, kann der Limiter direkt auf der Ebene der Warteschlange angegeben werden.

const { Queue, Worker } = require('bullmq');
const axios = require('axios');

const connection = { host: 'localhost', port: 6379 };

const proxyPool = [
  'http://user:[email protected]:8000',
  'http://user:[email protected]:8000',
  'http://user:[email protected]:8000',
];

const queue = new Queue('fetchQueue', { connection });

// Limiter: nicht mehr als 5 Aufgaben pro Sekunde in der Warteschlange
const worker = new Worker('fetchQueue', async job => {
  const proxy = proxyPool[job.id % proxyPool.length];
  try {
    const response = await axios.get(job.data.url, {
      proxy: false,
      httpsAgent: new (require('https-proxy-agent'))(proxy),
      timeout: 15000,
    });
    return response.status;
  } catch (err) {
    if (err.response && err.response.status === 429) {
      throw new Error('Rate limit erreicht, erneut mit einem anderen Proxy versuchen');
    }
    throw err;
  }
}, {
  connection,
  limiter: { max: 5, duration: 1000 },
  concurrency: proxyPool.length,
});

for (let i = 0; i < 100; i++) {
  queue.add('fetch', { url: `https://example.com/item/${i}` }, {
    attempts: 3,
    backoff: { type: 'exponential', delay: 5000 },
  });
}

Der Parameter limiter in BullMQ ist ein integrierter Mechanismus, der die Gesamtzahl der verarbeiteten Aufgaben über einen Zeitraum begrenzt, was praktisch ist, um das Anfrage-Limit auf Systemebene einzuhalten. Der Parameter concurrency, der der Größe des Proxy-Pools entspricht, stellt sicher, dass genau so viele Worker gleichzeitig arbeiten, wie es freie IPs gibt.

Dieser Ansatz wird häufig von Teams verwendet, die eigene Bots zur Automatisierung von Aktionen in TikTok oder Telegram schreiben - BullMQ integriert sich gut in den restlichen Node.js-Stack und erfordert keinen Wechsel zu Python für eine einzige Aufgabe.

Vergleich von Celery, RQ und BullMQ

Parameter Celery RQ BullMQ
Sprache Python Python Node.js
Broker Redis / RabbitMQ Nur Redis Nur Redis
Integrierter Rate-Limiter Nein (manuell implementiert) Nein Ja, out of the box
Einstiegshürde Mittel Niedrig Mittel
Geeignet für Große Produktionssysteme Kleine Projekte, Prototypen Node.js-Stack, Bots, Automatisierung

Strategien zur Lastverteilung nach IP

Round-robin, das in den obigen Beispielen verwendet wird, ist die einfachste Strategie, aber nicht immer die optimale. Hier sind einige Optionen, die je nach Aufgabe in Betracht gezogen werden sollten:

  • Round-robin — jede nächste Anfrage geht über den nächsten Proxy im Kreis. Geeignet, wenn alle IPs gleichwertig sind und die Limits auf ihnen identisch sind.
  • Sticky Session — ein Proxy wird für ein bestimmtes Konto oder eine Sitzung während der gesamten Bearbeitungszeit zugewiesen. Kritisch für soziale Netzwerke und Marktplätze, wo ein IP-Wechsel mitten in einer Sitzung verdächtig aussieht.
  • Weighted Distribution — ein Teil der Anfragen wird an teurere und zuverlässigere IPs (z. B. mobile) geleitet, ein Teil an billigere Datacenter-IPs, je nachdem, welche Domain bearbeitet wird.
  • Adaptive Throttling — die Geschwindigkeit des Sendens von Aufgaben wird automatisch reduziert, wenn der Prozentsatz der Fehler 429 oder Captchas steigt, und wird wiederhergestellt, wenn die Fehler verschwinden.

Für Aufgaben, bei denen es wichtig ist, sich als lebender Benutzer zu tarnen - beispielsweise beim Aufwärmen und Überprüfen von Konten in Instagram oder TikTok über Anti-Detect-Browser wie Dolphin Anty oder AdsPower - ist es besser, eine Sticky Session mit mobilen Proxys anzuwenden: Sie geraten seltener unter Verdacht von Anti-Bot-Systemen und verhalten sich wie normale Benutzer des mobilen Internets.

Für das massenhafte Sammeln von Daten von Websites, wo Geschwindigkeit und Volumen wichtiger sind als das Vertrauen des Algorithmus, bietet Round-robin mit residential Proxys ein Gleichgewicht zwischen Stabilität und Preis: IPs sehen aus wie echte Benutzer, können aber häufig ohne Risiko für eine bestimmte Sitzung gewechselt werden.

Überwachung von Fehlern 429 und Verboten

Eine Aufgabenwarteschlange ohne Überwachung ist eine Black Box: Es ist schwierig zu verstehen, warum einige Aufgaben fehlschlagen und welche IPs bereits unter Verdacht stehen. Das minimale Set an Metriken, das überwacht werden sollte:

  • Prozentsatz der Antworten mit dem Code 429 und 403 für jeden Proxy einzeln
  • Durchschnittliche Antwortzeit - ein Anstieg der Verzögerung geht oft einem Verbot voraus
  • Anzahl der Wiederholungsversuche (retries) pro Aufgabe
  • Länge der Aufgabenwarteschlange in Echtzeit - wenn sie wächst, schaffen die Worker nicht mehr

In Celery ist es praktisch, Flower zu verwenden - eine Weboberfläche zur Überwachung von Aufgaben und Workern in Echtzeit. In BullMQ gibt es das Bull Board mit ähnlicher Funktionalität. Für RQ gibt es das RQ Dashboard. Alle drei Tools zeigen den Status der Aufgaben, die Anzahl der fehlgeschlagenen Versuche an und ermöglichen es, schnell zu verstehen, welcher Proxy aus dem Pool vorübergehend aus der Rotation ausgeschlossen werden sollte.

Eine gute Praxis ist es, Proxys nach mehreren aufeinanderfolgenden Fehlern 429 von dieser IP automatisch als "in Quarantäne" für 15-30 Minuten zu kennzeichnen und sie erst nach einer erfolgreichen Testanfrage wieder in den Pool zurückzuführen. Dies verhindert, dass eine gesperrte IP weiterhin Aufgaben erhält und die Zeit der Worker vergeudet.

Fazit

Eine Aufgabenwarteschlange löst das Problem der Skalierung von Anfragen, schützt jedoch nicht von selbst vor dem Limit auf IP - dafür ist eine durchdachte Proxy-Rotation innerhalb von Celery, RQ oder BullMQ erforderlich. Die Wahl des Werkzeugs hängt vom Stack ab: Python-Projekte wählen häufiger Celery für die Produktion und RQ für einfache Aufgaben, Node.js-Entwickler - BullMQ mit seinem integrierten Rate-Limiter.

Unabhängig von der gewählten Warteschlange hängt das Ergebnis stark von der Qualität des Proxy-Pools ab. Wenn die Aufgabe ein schnelles massenhaftes Scraping ist, sollten Sie sich Datacenter-Proxys für Geschwindigkeit ansehen, und für die Arbeit mit sozialen Netzwerken und Marktplätzen, wo die Loyalität gegenüber Anti-Bot-Systemen wichtig ist, sind Residential oder mobile IPs geeignet - sie reduzieren den Prozentsatz von Fehlern 429 und Verboten, was direkt die Anzahl der Wiederholungsversuche verringert und die Ausführung der gesamten Aufgabenwarteschlange beschleunigt.