← Voltar ao blog

Salsão, RQ ou BullMQ: como criar uma fila de tarefas com proxy e contornar o limite de requisições por IP

Analisamos a arquitetura de filas de tarefas com rotação de proxy no Celery, RQ e BullMQ - como distribuir a carga entre endereços IP e evitar limite de taxa.

📅20 de outubro de 2026

Quando um parser ou bot envia centenas de solicitações por minuto de um único IP, o servidor rapidamente ativa o limite de taxa, e a partir daí — captcha ou banimento. A solução é uma fila de tarefas que distribui as solicitações entre trabalhadores e proxies. Neste artigo, vamos explorar como essa arquitetura funciona no Aipo, RQ e BullMQ, e como gerenciar adequadamente o limite de solicitações para cada endereço IP.

Por que usar uma fila de tarefas ao trabalhar com proxies

Se a tarefa é coletar 50.000 produtos da Wildberries ou verificar o status de 10.000 contas do Instagram, a execução sequencial das solicitações levará horas, e a execução paralela sem controle rapidamente resultará em banimento por IP. A fila de tarefas (task queue) resolve ambos os problemas: ela divide um grande volume de trabalho em pequenas tarefas, distribui-as entre vários trabalhadores e permite vincular um proxy separado a cada trabalhador.

A arquitetura geralmente se parece com isso: um broker de mensagens (Redis ou RabbitMQ) armazena a fila de tarefas, os trabalhadores retiram as tarefas da fila e as executam, cada trabalhador usa seu próprio proxy ou rotaciona proxies de um pool. Isso permite manter a carga em cada IP dentro de limites seguros, mesmo que o volume total de solicitações seja enorme.

Essa abordagem é utilizada não apenas por desenvolvedores de parsers, mas também por equipes que automatizam a verificação de contas de anúncios do Facebook Ads, monitoram preços no Ozon ou coletam dados de dezenas de sites ao mesmo tempo. Sem uma fila e uma distribuição adequada por IP, qualquer sistema desse tipo está fadado a bloqueios no primeiro dia de operação.

Problema do limite de solicitações por um IP

A maioria dos sites e APIs possui proteção contra carga excessiva. Isso pode se manifestar de várias maneiras: o servidor retorna HTTP 429 (Too Many Requests), exibe um captcha, bane temporariamente o IP por 10-60 minutos ou o adiciona permanentemente à lista negra. Os limites variam muito entre diferentes plataformas — por exemplo, a API da Wildberries permite um número limitado de solicitações por segundo de um único endereço, enquanto o Instagram reage a atividades suspeitas após 15-20 ações consecutivas.

O principal erro ao construir uma fila é executar todos os trabalhadores através do mesmo IP. Mesmo que as tarefas estejam distribuídas entre 20 trabalhadores, se todos eles usam um único proxy ou não usam nenhum, o limite para esse IP será atingido tão rapidamente quanto na execução sequencial. Portanto, o número de trabalhadores deve corresponder ao número de endereços IP disponíveis no pool de proxies.

Para tarefas com alta frequência de solicitações para um único domínio, é melhor usar proxies de data center — eles oferecem baixa latência e são adequados para scraping paralelo em larga escala, onde a velocidade é mais importante do que se disfarçar como um usuário comum. Se o objetivo é contornar proteções mais rigorosas, como sistemas anti-bot de marketplaces ou redes sociais, são necessários IPs que se pareçam com usuários comuns, ou seja, residenciais ou móveis.

Aipo: arquitetura e rotação de proxies no código

Aipo é a fila de tarefas mais madura e popular no ecossistema Python. Ela funciona sobre um broker (geralmente Redis ou RabbitMQ) e suporta configuração flexível de concorrência, tentativas de repetição e prioridades de tarefas. Para rotação de proxies, é conveniente armazenar um pool de IPs no Redis e atribuir um proxy a cada tarefa ao colocá-la na fila.

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 através do Redis, para não sobrecarregar um único IP
    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 limited"))
        return response.status_code
    except requests.exceptions.RequestException as exc:
        raise self.retry(exc=exc)

O ponto chave é a função get_proxy(), que através de um contador Redis fornece o próximo proxy do pool em um ciclo. Isso garante que a carga seja distribuída uniformemente entre todos os IPs, e não se concentre em um único. O parâmetro max_retries e o tratamento do código 429 permitem que a tarefa seja automaticamente reenviada com um proxy diferente, se o IP atual receber uma limitação.

Para controlar a concorrência no nível dos trabalhadores, o Aipo permite limitar o número de tarefas simultâneas através do parâmetro --concurrency ao iniciar o trabalhador, bem como através de filas com diferentes prioridades (por exemplo, uma fila separada para tarefas lentas com limite de taxa rigoroso).

RQ (Redis Queue): uma alternativa simples

RQ é uma biblioteca mais leve do que o Aipo, sem o broker RabbitMQ e com configuração mínima. Ela é adequada se o projeto for pequeno e não exigir roteamento complexo de tarefas, mas apenas uma fila com trabalhadores e a capacidade de vincular proxies a tarefas.

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:
        # Recolocação na fila com atraso
        q.enqueue_in(60, fetch_with_proxy, url)
        return None
    return response.status_code

# Colocando tarefas na fila
urls = ["https://example.com/page1", "https://example.com/page2"]
for url in urls:
    q.enqueue(fetch_with_proxy, url)

No RQ, a rotação através de itertools.cycle funciona dentro do processo do trabalhador — é mais simples do que um contador Redis, mas não escala bem com muitos trabalhadores paralelos, pois cada trabalhador começa o ciclo do início. Para pequenos projetos com 1-5 trabalhadores, isso é suficiente; para projetos maiores, é melhor voltar a um contador centralizado no Redis, como no exemplo com o Aipo.

O RQ é bem adequado para desenvolvedores que já usam Redis em seus projetos e não querem levantar um broker de mensagens separado. A desvantagem é que há menos mecanismos integrados de priorização e monitoramento em comparação com o Aipo, portanto, para sistemas de produção com centenas de milhares de tarefas por dia, o Aipo é frequentemente escolhido.

BullMQ: fila em Node.js com limite por IP

BullMQ é o padrão para filas de tarefas no ecossistema Node.js, construído sobre Redis e possui um limitador de taxa embutido, o que é especialmente conveniente para controlar o limite de solicitações por IP. Em vez de contar manualmente as solicitações, você pode especificar o limitador diretamente no nível da fila.

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

// Limitador: no máximo 5 tarefas por segundo na fila
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 limited, retrying with another proxy');
    }
    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 },
  });
}

O parâmetro limiter no BullMQ é um mecanismo embutido que limita o número total de tarefas processadas em um intervalo de tempo, o que é conveniente para respeitar o limite de solicitações em todo o sistema. E o parâmetro concurrency, igual ao tamanho do pool de proxies, garante que exatamente o número de trabalhadores disponíveis esteja em operação ao mesmo tempo.

Essa abordagem é frequentemente utilizada por equipes que escrevem seus próprios bots para automatizar ações no TikTok ou Telegram — o BullMQ se integra bem com o restante do stack Node.js e não requer a mudança para Python apenas para uma tarefa.

Comparação entre Aipo, RQ e BullMQ

Parâmetro Aipo RQ BullMQ
Linguagem Python Python Node.js
Broker Redis / RabbitMQ Apenas Redis Apenas Redis
Limitador de taxa embutido Não (implementado manualmente) Não Sim, de fábrica
Barreira de entrada Média Baixa Média
Adequado para Grandes sistemas de produção Pequenos projetos, protótipos Stack Node.js, bots, automação

Estratégias de distribuição de carga por IP

O round-robin, que é utilizado nos exemplos acima, é a estratégia mais simples, mas nem sempre é a ideal. Aqui estão algumas opções que vale a pena considerar dependendo da tarefa:

  • Round-robin — cada solicitação seguinte passa pelo próximo proxy em um ciclo. Adequado quando todos os IPs são equivalentes e os limites são os mesmos.
  • Sticky session — um proxy é vinculado a uma conta ou sessão específica durante todo o tempo de uso. Crítico para redes sociais e marketplaces, onde mudar de IP no meio de uma sessão parece suspeito.
  • Weighted distribution — parte das solicitações é direcionada a IPs mais caros e confiáveis (por exemplo, móveis), enquanto parte vai para IPs de data center mais baratos, dependendo do domínio que está sendo processado.
  • Adaptive throttling — a velocidade de envio de tarefas é automaticamente reduzida se a porcentagem de erros 429 ou captchas aumentar, e é restaurada quando os erros desaparecem.

Para tarefas onde é importante se disfarçar como um usuário real — por exemplo, aquecer e verificar contas no Instagram ou TikTok através de navegadores anti-detectores como Dolphin Anty ou AdsPower — é melhor aplicar sticky session com proxies móveis: eles raramente são suspeitos para sistemas anti-bot e se comportam como usuários normais da internet móvel.

Para coleta massiva de dados de sites, onde a velocidade e o volume são mais importantes do que a confiança do algoritmo, round-robin com proxies residenciais oferece um equilíbrio entre estabilidade e preço: os IPs parecem usuários reais, mas podem ser trocados frequentemente sem risco para uma sessão específica.

Monitoramento de erros 429 e banimentos

Uma fila de tarefas sem monitoramento é uma caixa-preta: é difícil entender por que parte das tarefas falha e quais IPs já estão sob suspeita. O conjunto mínimo de métricas que vale a pena monitorar:

  • Porcentagem de respostas com código 429 e 403 para cada proxy separadamente
  • Tempo médio de resposta — um aumento na latência frequentemente precede um banimento
  • Número de tentativas (retries) por tarefa
  • Comprimento da fila de tarefas em tempo real — se ela está crescendo, os trabalhadores não estão conseguindo acompanhar

No Aipo, é conveniente usar o Flower — uma interface web para monitoramento de tarefas e trabalhadores em tempo real. No BullMQ, há o Bull Board com funcionalidade semelhante. Para o RQ, existe o RQ Dashboard. Todas as três ferramentas mostram o status das tarefas, o número de tentativas malsucedidas e permitem entender rapidamente qual proxy do pool deve ser temporariamente excluído da rotação.

Uma boa prática é automaticamente marcar proxies como "em quarentena" por 15-30 minutos após várias falhas consecutivas de 429 desse IP, e retorná-los ao pool apenas após uma solicitação de teste bem-sucedida. Isso evita a situação em que um IP banido continua recebendo tarefas e desperdiçando o tempo dos trabalhadores.

Conclusão

A fila de tarefas resolve o problema de escalabilidade das solicitações, mas por si só não protege contra limites de IP — para isso, é necessária uma rotação de proxies bem planejada dentro do Aipo, RQ ou BullMQ. A escolha da ferramenta depende do stack: projetos em Python geralmente optam pelo Aipo para produção e RQ para tarefas simples, enquanto desenvolvedores Node.js preferem o BullMQ com seu limitador de taxa embutido.

Independentemente da fila escolhida, o resultado depende muito da qualidade do próprio pool de proxies. Se a tarefa é um scraping massivo rápido, considere usar proxies de data center para velocidade, e para trabalhar com redes sociais e marketplaces, onde a lealdade dos sistemas anti-bot é importante, proxies residenciais ou móveis são adequados — eles reduzem a porcentagem de erros 429 e banimentos, o que diretamente diminui o número de tentativas e acelera a execução de toda a fila de tarefas.