Quando você faz parsing de milhares de páginas de marketplaces, envia requisições massivas de API ou automatiza o trabalho com centenas de contas, a distribuição correta de carga através de proxies torna-se criticamente importante. Sem um balanceamento adequado, você enfrentará bloqueios, timeouts e queda de desempenho. Neste guia, analisaremos a arquitetura de distribuição de carga, algoritmos de balanceamento e estratégias práticas para sistemas de alta carga.
O material é destinado a desenvolvedores e especialistas técnicos que trabalham com parsing de dados, automação de requisições de API ou gerenciam grandes pools de proxies para tarefas de negócios.
Por que é necessário balanceamento de carga através de proxy
O balanceamento de carga através de proxies resolve vários problemas críticos de sistemas de alta carga. O primeiro e principal — proteção contra bloqueios. Quando você envia milhares de requisições para um único recurso (marketplace, API de rede social, mecanismo de busca), o servidor de destino detecta atividade anormalmente alta de um único endereço IP e o bloqueia. A distribuição de requisições entre dezenas ou centenas de proxies torna sua atividade semelhante às ações de usuários comuns.
O segundo problema — desempenho. Um servidor proxy tem largura de banda limitada (geralmente 100-1000 Mbps) e pode processar um número limitado de conexões simultâneas. Ao fazer parsing de 10.000 páginas por minuto ou envio massivo de requisições de API, um único proxy se tornará um gargalo do sistema. O balanceamento permite escalar horizontalmente a largura de banda, adicionando novos proxies ao pool.
A terceira tarefa — confiabilidade. Se um dos proxies falhar (falha técnica, bloqueio, expiração do período de aluguel), o sistema redireciona automaticamente o tráfego para proxies funcionais. Sem mecanismo de balanceamento e health checks, a falha de um proxy pode parar todo o sistema.
Exemplo real: Ao fazer parsing do Wildberries para monitorar preços de concorrentes, você precisa processar 50.000 produtos a cada hora. Isso é aproximadamente 14 requisições por segundo. Um proxy suportará essa carga, mas o Wildberries bloqueará o IP após 100-200 requisições do mesmo endereço. Distribuindo requisições entre 20 proxies residenciais, você reduz a carga em cada IP para 0,7 requisições por segundo — isso parece atividade de um usuário comum.
A quarta razão — distribuição geográfica. Muitos serviços mostram conteúdo ou preços diferentes dependendo da região do usuário. O balanceamento entre proxies de diferentes países e cidades permite coletar dados simultaneamente de todas as regiões-alvo. Por exemplo, para monitorar preços no Ozon, você precisa de proxies de Moscou, São Petersburgo, Ecaterimburgo e outras grandes cidades.
Arquitetura do sistema de distribuição de carga
A arquitetura clássica de um sistema de balanceamento de carga através de proxies consiste em vários componentes. No nível superior está o balanceador (load balancer) — um módulo de software que recebe tarefas de entrada (requisições de parsing, chamadas de API) e as distribui entre os proxies disponíveis. O balanceador pode funcionar como um serviço separado ou estar integrado na aplicação.
O segundo componente — gerenciador de pool de proxies (proxy pool manager). Ele armazena a lista de todos os proxies disponíveis com suas características: endereço IP, porta, protocolo (HTTP/SOCKS5), localização geográfica, tipo (residencial, móvel, data center), estado atual (ativo, bloqueado, em verificação). O gerenciador de pool é responsável por adicionar novos proxies, remover os que não funcionam e verificação periódica de disponibilidade.
O terceiro elemento — sistema de monitoramento e health checks. Ele verifica constantemente a operacionalidade de cada proxy: envia requisições de teste, mede o tempo de resposta, verifica o sucesso da conexão. Se o proxy não responder ou retornar erros, o sistema o marca como indisponível e o exclui temporariamente da rotação.
| Componente | Função | Tecnologias |
|---|---|---|
| Load Balancer | Distribuição de requisições entre proxies | HAProxy, Nginx, bibliotecas Python/Node.js |
| Proxy Pool Manager | Gerenciamento da lista de proxies | Redis, PostgreSQL, armazenamentos in-memory |
| Health Check System | Verificação de disponibilidade de proxies | Scheduled tasks, Celery, cron |
| Rate Limiter | Controle de frequência de requisições | Algoritmos token bucket, leaky bucket |
| Monitoring & Metrics | Coleta de métricas de desempenho | Prometheus, Grafana, ELK Stack |
O quarto componente — rate limiter (limitador de frequência de requisições). Ele monitora para que cada proxy não exceda a frequência permitida de requisições ao recurso de destino. Por exemplo, se você faz parsing do Instagram e sabe que a plataforma bloqueia IPs após 60 requisições por minuto, o rate limiter não permitirá enviar mais de 50 requisições por minuto através de um proxy, deixando uma margem de segurança.
O quinto elemento — sistema de métricas e análise. Ele coleta dados sobre o desempenho de cada proxy: número de requisições bem-sucedidas, número de erros, tempo médio de resposta, porcentagem de bloqueios. Esses dados são usados para otimizar algoritmos de balanceamento e identificar proxies problemáticos.
Algoritmos de balanceamento: Round Robin, Least Connections, Weighted
O algoritmo Round Robin — o método mais simples e comum de balanceamento. Ele percorre sequencialmente os proxies da lista: a primeira requisição vai pelo proxy nº 1, a segunda pelo proxy nº 2, a terceira pelo proxy nº 3, e assim por diante. Quando a lista termina, o balanceador retorna ao início. Este algoritmo distribui uniformemente a carga se todos os proxies tiverem o mesmo desempenho.
class RoundRobinBalancer:
def __init__(self, proxies):
self.proxies = proxies
self.current_index = 0
def get_next_proxy(self):
proxy = self.proxies[self.current_index]
self.current_index = (self.current_index + 1) % len(self.proxies)
return proxy
# Uso
proxies = [
"http://proxy1.example.com:8080",
"http://proxy2.example.com:8080",
"http://proxy3.example.com:8080"
]
balancer = RoundRobinBalancer(proxies)
for i in range(10):
proxy = balancer.get_next_proxy()
print(f"Request {i+1} → {proxy}")
O algoritmo Least Connections (menor número de conexões) escolhe o proxy com o menor número de conexões ativas no momento atual. Isso é útil quando as requisições têm diferentes durações de execução. Por exemplo, ao fazer parsing, uma requisição pode ser processada em 100 ms, e outra — em 5 segundos (se a página carregar lentamente). Least Connections direciona automaticamente novas requisições para proxies menos carregados.
class LeastConnectionsBalancer:
def __init__(self, proxies):
self.proxies = {proxy: 0 for proxy in proxies}
def get_next_proxy(self):
# Escolhemos o proxy com o menor número de conexões
proxy = min(self.proxies.items(), key=lambda x: x[1])[0]
self.proxies[proxy] += 1
return proxy
def release_proxy(self, proxy):
# Diminuímos o contador ao concluir a requisição
self.proxies[proxy] -= 1
# Uso com gerenciador de contexto
balancer = LeastConnectionsBalancer(proxies)
def make_request(url):
proxy = balancer.get_next_proxy()
try:
# Executamos a requisição através do proxy escolhido
response = requests.get(url, proxies={"http": proxy})
return response
finally:
balancer.release_proxy(proxy)
Weighted Round Robin (round robin ponderado) estende o Round Robin clássico, atribuindo a cada proxy um peso dependendo de seu desempenho ou qualidade. Proxies com maior peso recebem mais requisições. Isso é útil quando você tem proxies de diferentes qualidades: por exemplo, proxies residenciais premium com alta velocidade e proxies baratos de data centers com limitações.
class WeightedRoundRobinBalancer:
def __init__(self, weighted_proxies):
# weighted_proxies = [(proxy, weight), ...]
self.proxies = []
for proxy, weight in weighted_proxies:
# Adicionamos o proxy à lista weight vezes
self.proxies.extend([proxy] * weight)
self.current_index = 0
def get_next_proxy(self):
proxy = self.proxies[self.current_index]
self.current_index = (self.current_index + 1) % len(self.proxies)
return proxy
# Uso com pesos
weighted_proxies = [
("http://premium-proxy1.com:8080", 5), # alta qualidade
("http://premium-proxy2.com:8080", 5),
("http://cheap-proxy1.com:8080", 2), # baixa qualidade
("http://cheap-proxy2.com:8080", 1) # qualidade muito baixa
]
balancer = WeightedRoundRobinBalancer(weighted_proxies)
O algoritmo Random (escolha aleatória) escolhe um proxy aleatoriamente da lista de disponíveis. É mais simples de implementar do que Round Robin e funciona bem com um grande número de proxies (100+). A distribuição aleatória se equilibra automaticamente com volume suficiente de requisições. A desvantagem — possíveis períodos curtos de carga desigual.
IP Hash — algoritmo que escolhe o proxy com base no hash da URL de destino ou outro parâmetro. Isso garante que requisições para o mesmo recurso sempre passem pelo mesmo proxy. Útil para sites que usam sessões ou requerem sequência de requisições do mesmo IP (por exemplo, autenticação + obtenção de dados).
Gerenciamento de pools de proxy: rotação e health checks
O gerenciamento eficaz de pool de proxies requer monitoramento constante do estado de cada proxy e rotação automática. Health check — é uma verificação periódica da disponibilidade do proxy enviando uma requisição de teste. Geralmente é usado um simples GET request para um serviço confiável (por exemplo, httpbin.org ou endpoint próprio), que retorna informações sobre o endereço IP.
import requests
import time
from datetime import datetime
class ProxyHealthChecker:
def __init__(self, test_url="http://httpbin.org/ip", timeout=10):
self.test_url = test_url
self.timeout = timeout
def check_proxy(self, proxy_url):
"""Verifica a operacionalidade do proxy"""
try:
start_time = time.time()
response = requests.get(
self.test_url,
proxies={"http": proxy_url, "https": proxy_url},
timeout=self.timeout
)
response_time = time.time() - start_time
if response.status_code == 200:
return {
"status": "healthy",
"response_time": response_time,
"timestamp": datetime.now(),
"ip": response.json().get("origin")
}
else:
return {
"status": "unhealthy",
"error": f"HTTP {response.status_code}",
"timestamp": datetime.now()
}
except requests.exceptions.Timeout:
return {
"status": "unhealthy",
"error": "timeout",
"timestamp": datetime.now()
}
except Exception as e:
return {
"status": "unhealthy",
"error": str(e),
"timestamp": datetime.now()
}
# Uso
checker = ProxyHealthChecker()
proxies = ["http://proxy1.com:8080", "http://proxy2.com:8080"]
for proxy in proxies:
result = checker.check_proxy(proxy)
print(f"{proxy}: {result['status']} ({result.get('response_time', 'N/A')}s)")
O sistema de gerenciamento de pool deve marcar automaticamente proxies não funcionais como indisponíveis e repetir periodicamente a verificação. Uma estratégia comum — circuit breaker pattern: após três verificações malsucedidas seguidas, o proxy é excluído do pool por 5-10 minutos, depois é verificado novamente. Se a verificação for bem-sucedida, o proxy retorna ao pool ativo.
class ProxyPoolManager:
def __init__(self, health_checker, max_failures=3, cooldown_seconds=300):
self.health_checker = health_checker
self.max_failures = max_failures
self.cooldown_seconds = cooldown_seconds
self.proxies = {} # {proxy_url: ProxyInfo}
def add_proxy(self, proxy_url, metadata=None):
"""Adiciona proxy ao pool"""
self.proxies[proxy_url] = {
"url": proxy_url,
"status": "active",
"failures": 0,
"last_check": None,
"cooldown_until": None,
"metadata": metadata or {}
}
def get_active_proxies(self):
"""Retorna lista de proxies ativos"""
now = datetime.now()
active = []
for proxy_url, info in self.proxies.items():
# Verificamos se o proxy não está em cooldown
if info["cooldown_until"] and now < info["cooldown_until"]:
continue
if info["status"] == "active":
active.append(proxy_url)
return active
def mark_failure(self, proxy_url):
"""Marca tentativa malsucedida de uso do proxy"""
if proxy_url not in self.proxies:
return
info = self.proxies[proxy_url]
info["failures"] += 1
if info["failures"] >= self.max_failures:
# Colocamos em cooldown
info["status"] = "cooldown"
info["cooldown_until"] = datetime.now() + timedelta(seconds=self.cooldown_seconds)
print(f"Proxy {proxy_url} moved to cooldown until {info['cooldown_until']}")
def mark_success(self, proxy_url):
"""Marca uso bem-sucedido do proxy"""
if proxy_url not in self.proxies:
return
info = self.proxies[proxy_url]
info["failures"] = 0
info["status"] = "active"
info["cooldown_until"] = None
Rotação de proxies — é uma estratégia de troca automática de proxies em intervalos determinados ou após um número determinado de requisições. Existem várias abordagens: rotação por tempo (troca a cada 5-10 minutos), rotação por número de requisições (troca após 100-500 requisições), rotação por sessões (um proxy por sessão de parsing). A escolha da estratégia depende dos requisitos do site de destino.
Dica: Para parsing de marketplaces (Wildberries, Ozon), a rotação ideal é por número de requisições: 50-100 requisições por proxy, depois troca. Para trabalhar com APIs de redes sociais (Instagram, Facebook), é melhor usar rotação por sessões: um proxy para todo o ciclo autenticação → ações → saída.
Rate limiting e controle de frequência de requisições
Rate limiting — componente criticamente importante do sistema de balanceamento de carga. Ele previne a superação da frequência permitida de requisições ao recurso de destino, o que poderia levar ao bloqueio do proxy. Existem dois algoritmos principais: Token Bucket (balde de tokens) e Leaky Bucket (balde furado).
Token Bucket funciona da seguinte forma: cada proxy tem um "balde" virtual com tokens. Cada token dá direito a uma requisição. O balde é gradualmente preenchido com tokens a uma velocidade definida (por exemplo, 10 tokens por segundo). A capacidade máxima do balde é limitada (por exemplo, 50 tokens). Quando chega uma requisição, o sistema verifica a disponibilidade de tokens: se houver — pega um token e executa a requisição, se não — aguarda o aparecimento de um novo token.
import time
from threading import Lock
class TokenBucketRateLimiter:
def __init__(self, rate, capacity):
"""
rate: número de tokens por segundo
capacity: número máximo de tokens no balde
"""
self.rate = rate
self.capacity = capacity
self.tokens = capacity
self.last_update = time.time()
self.lock = Lock()
def _add_tokens(self):
"""Adiciona tokens com base no tempo decorrido"""
now = time.time()
elapsed = now - self.last_update
new_tokens = elapsed * self.rate
self.tokens = min(self.capacity, self.tokens + new_tokens)
self.last_update = now
def acquire(self, tokens=1):
"""Tenta obter o número especificado de tokens"""
with self.lock:
self._add_tokens()
if self.tokens >= tokens:
self.tokens -= tokens
return True
return False
def wait_and_acquire(self, tokens=1):
"""Aguarda o aparecimento de tokens e os obtém"""
while not self.acquire(tokens):
# Calculamos o tempo de espera
wait_time = (tokens - self.tokens) / self.rate
time.sleep(wait_time)
# Uso para cada proxy
proxy_limiters = {
"http://proxy1.com:8080": TokenBucketRateLimiter(rate=10, capacity=50),
"http://proxy2.com:8080": TokenBucketRateLimiter(rate=10, capacity=50)
}
def make_request_with_limit(url, proxy_url):
limiter = proxy_limiters[proxy_url]
limiter.wait_and_acquire() # Aguardamos disponibilidade do token
response = requests.get(url, proxies={"http": proxy_url})
return response
Leaky Bucket (balde furado) funciona de forma diferente: as requisições são colocadas em uma fila (balde), que "vaza" a uma velocidade constante. Se o balde transbordar, novas requisições são rejeitadas ou colocadas em espera. Este algoritmo garante uma distribuição mais uniforme da carga ao longo do tempo, mas pode levar a atrasos em picos de atividade.
Para diferentes plataformas de destino são necessários diferentes limites. A API do Instagram permite aproximadamente 200 requisições por hora por IP (cerca de 3,3 requisições por minuto). O Wildberries bloqueia após 100-200 requisições por minuto do mesmo IP. O Google Search permite 10-20 requisições por minuto. Seu sistema de rate limiting deve considerar essas limitações e adicionar uma margem de segurança (geralmente 20-30%).
| Plataforma | Limite de requisições | Configuração recomendada |
|---|---|---|
| ~200 requisições/hora | 2-3 requisições/minuto (com margem) | |
| Wildberries | 100-200 requisições/minuto | 60-80 requisições/minuto |
| Google Search | 10-20 requisições/minuto | 8-12 requisições/minuto |
| Ozon | 50-100 requisições/minuto | 30-50 requisições/minuto |
| Facebook API | 200 requisições/hora | 2-3 requisições/minuto |
Monitoramento de desempenho e escalabilidade automática
Um sistema eficaz de balanceamento de carga requer monitoramento constante de métricas-chave. O primeiro grupo de métricas — desempenho do proxy: tempo médio de resposta (response time), porcentagem de requisições bem-sucedidas (success rate), número de timeouts, número de erros 4xx e 5xx. Esses dados permitem identificar proxies problemáticos e otimizar algoritmos de balanceamento.
class ProxyMetrics:
def __init__(self):
self.metrics = {} # {proxy_url: metrics_dict}
def record_request(self, proxy_url, response_time, status_code, success):
"""Registra métricas da requisição"""
if proxy_url not in self.metrics:
self.metrics[proxy_url] = {
"total_requests": 0,
"successful_requests": 0,
"failed_requests": 0,
"total_response_time": 0,
"timeouts": 0,
"errors_4xx": 0,
"errors_5xx": 0
}
m = self.metrics[proxy_url]
m["total_requests"] += 1
if success:
m["successful_requests"] += 1
m["total_response_time"] += response_time
else:
m["failed_requests"] += 1
if status_code == 0: # timeout
m["timeouts"] += 1
elif 400 <= status_code < 500:
m["errors_4xx"] += 1
elif 500 <= status_code < 600:
m["errors_5xx"] += 1
def get_success_rate(self, proxy_url):
"""Retorna porcentagem de requisições bem-sucedidas"""
m = self.metrics.get(proxy_url, {})
total = m.get("total_requests", 0)
if total == 0:
return 0
return (m.get("successful_requests", 0) / total) * 100
def get_avg_response_time(self, proxy_url):
"""Retorna tempo médio de resposta"""
m = self.metrics.get(proxy_url, {})
successful = m.get("successful_requests", 0)
if successful == 0:
return 0
return m.get("total_response_time", 0) / successful
def get_report(self, proxy_url):
"""Retorna relatório completo do proxy"""
m = self.metrics.get(proxy_url, {})
return {
"proxy": proxy_url,
"total_requests": m.get("total_requests", 0),
"success_rate": self.get_success_rate(proxy_url),
"avg_response_time": self.get_avg_response_time(proxy_url),
"timeouts": m.get("timeouts", 0),
"errors_4xx": m.get("errors_4xx", 0),
"errors_5xx": m.get("errors_5xx", 0)
}
O segundo grupo de métricas — desempenho geral do sistema: número total de requisições por segundo (RPS — requests per second), latência média (latency), tamanho da fila de requisições, número de proxies ativos, porcentagem de uso do pool de proxies. Essas métricas mostram se o sistema está lidando com a carga ou se é necessário escalonamento.
O escalonamento automático (auto-scaling) permite adicionar ou remover proxies dinamicamente dependendo da carga. Estratégia simples: se a carga média do pool exceder 80% durante 5 minutos, o sistema adiciona automaticamente novos proxies. Se a carga cair abaixo de 30% durante 15 minutos, o sistema remove proxies excedentes para economizar recursos.
Exemplo de configuração de monitoramento: Para fazer parsing de 100.000 produtos do Wildberries por hora (aproximadamente 28 requisições por segundo), você precisará de no mínimo 30-40 proxies com limite de 60 requisições por minuto por proxy. Configure alertas: se o success rate cair abaixo de 85% ou o response time médio exceder 3 segundos, o sistema deve adicionar automaticamente 10-15 proxies de reserva do pool.
Para visualização de métricas, use Grafana com Prometheus ou ELK Stack. Crie dashboards com gráficos: RPS ao longo do tempo, distribuição de response time, top 10 proxies mais rápidos/lentos, mapa de erros por tipos. Isso permitirá identificar rapidamente problemas e otimizar o sistema.
Implementação prática em Python e Node.js
Vamos considerar uma implementação completa de um sistema de balanceamento de carga em Python usando bibliotecas populares. Este exemplo combina todos os componentes descritos: balanceador, gerenciador de pool, health checks, rate limiting e monitoramento.
import requests
import time
import random
from datetime import datetime, timedelta
from threading import Lock, Thread
from collections import defaultdict
class ProxyLoadBalancer:
def __init__(self, proxies, algorithm="round_robin", rate_limit=10):
"""
proxies: lista de proxies [{"url": "...", "weight": 1}, ...]
algorithm: round_robin, least_connections, weighted, random
rate_limit: número máximo de requisições por segundo por proxy
"""
self.proxies = proxies
self.algorithm = algorithm
self.rate_limit = rate_limit
# Estado do balanceador
self.current_index = 0
self.connections = defaultdict(int)
self.rate_limiters = {}
self.metrics = defaultdict(lambda: {
"total": 0, "success": 0, "failed": 0,
"response_times": [], "last_check": None
})
# Inicialização dos rate limiters
for proxy in proxies:
proxy_url = proxy["url"]
self.rate_limiters[proxy_url] = TokenBucketRateLimiter(
rate=rate_limit,
capacity=rate_limit * 5
)
self.lock = Lock()
# Início do health check em background
self.health_check_thread = Thread(target=self._health_check_loop, daemon=True)
self.health_check_thread.start()
def get_next_proxy(self):
"""Escolhe o próximo proxy de acordo com o algoritmo"""
with self.lock:
if self.algorithm == "round_robin":
return self._round_robin()
elif self.algorithm == "least_connections":
return self._least_connections()
elif self.algorithm == "weighted":
return self._weighted_random()
elif self.algorithm == "random":
return random.choice(self.proxies)["url"]
def _round_robin(self):
proxy = self.proxies[self.current_index]["url"]
self.current_index = (self.current_index + 1) % len(self.proxies)
return proxy
def _least_connections(self):
min_conn = min(self.connections.values()) if self.connections else 0
candidates = [p["url"] for p in self.proxies if self.connections[p["url"]] == min_conn]
return random.choice(candidates)
def _weighted_random(self):
weights = [p.get("weight", 1) for p in self.proxies]
return random.choices(self.proxies, weights=weights)[0]["url"]
def make_request(self, url, method="GET", **kwargs):
"""Executa requisição através do balanceador"""
proxy_url = self.get_next_proxy()
# Aguardamos disponibilidade do rate limit
self.rate_limiters[proxy_url].wait_and_acquire()
# Aumentamos o contador de conexões
with self.lock:
self.connections[proxy_url] += 1
try:
start_time = time.time()
response = requests.request(
method,
url,
proxies={"http": proxy_url, "https": proxy_url},
timeout=kwargs.get("timeout", 30),
**kwargs
)
response_time = time.time() - start_time
# Registramos métricas
self._record_metrics(proxy_url, response_time, True, response.status_code)
return response
except Exception as e:
self._record_metrics(proxy_url, 0, False, 0)
raise
finally:
# Diminuímos o contador de conexões
with self.lock:
self.connections[proxy_url] -= 1
def _record_metrics(self, proxy_url, response_time, success, status_code):
"""Registra métricas da requisição"""
with self.lock:
m = self.metrics[proxy_url]
m["total"] += 1
if success:
m["success"] += 1
m["response_times"].append(response_time)
# Armazenamos apenas os últimos 1000 valores
if len(m["response_times"]) > 1000:
m["response_times"].pop(0)
else:
m["failed"] += 1
def _health_check_loop(self):
"""Verificação em background da operacionalidade dos proxies"""
while True:
for proxy in self.proxies:
proxy_url = proxy["url"]
try:
response = requests.get(
"http://httpbin.org/ip",
proxies={"http": proxy_url},
timeout=10
)
with self.lock:
self.metrics[proxy_url]["last_check"] = datetime.now()
proxy["status"] = "healthy" if response.status_code == 200 else "unhealthy"
except:
with self.lock:
proxy["status"] = "unhealthy"
time.sleep(60) # Verificação a cada minuto
def get_stats(self):
"""Retorna estatísticas de todos os proxies"""
stats = []
with self.lock:
for proxy in self.proxies:
proxy_url = proxy["url"]
m = self.metrics[proxy_url]
avg_response_time = (
sum(m["response_times"]) / len(m["response_times"])
if m["response_times"] else 0
)
success_rate = (
(m["success"] / m["total"] * 100)
if m["total"] > 0 else 0
)
stats.append({
"proxy": proxy_url,
"status": proxy.get("status", "unknown"),
"total_requests": m["total"],
"success_rate": round(success_rate, 2),
"avg_response_time": round(avg_response_time, 3),
"active_connections": self.connections[proxy_url]
})
return stats
Uso deste balanceador para parsing de marketplace:
# Configuração do balanceador
proxies = [
{"url": "http://proxy1.example.com:8080", "weight": 5},
{"url": "http://proxy2.example.com:8080", "weight": 5},
{"url": "http://proxy3.example.com:8080", "weight": 3},
{"url": "http://proxy4.example.com:8080", "weight": 2}
]
balancer = ProxyLoadBalancer(
proxies=proxies,
algorithm="weighted",
rate_limit=60 # 60 requisições por minuto por proxy
)
# Parsing de lista de produtos
product_urls = [f"https://www.wildberries.ru/catalog/{i}/detail.aspx" for i in range(1000)]
results = []
for url in product_urls:
try:
response = balancer.make_request(url)
# Processamento da resposta
results.append({"url": url, "status": "success", "data": response.text})
except Exception as e:
results.append({"url": url, "status": "error", "error": str(e)})
# A cada 100 requisições exibimos estatísticas
if len(results) % 100 == 0:
stats = balancer.get_stats()
for stat in stats:
print(f"{stat['proxy']}: {stat['success_rate']}% success, "
f"{stat['avg_response_time']}s avg response")
# Estatísticas finais
print("\n=== Final Statistics ===")
for stat in balancer.get_stats():
print(f"{stat['proxy']}:")
print(f" Total requests: {stat['total_requests']}")
print(f" Success rate: {stat['success_rate']}%")
print(f" Avg response time: {stat['avg_response_time']}s")
print(f" Status: {stat['status']}")
Para Node.js, você pode usar uma arquitetura semelhante com as bibliotecas axios para requisições HTTP e node-rate-limiter para controle de frequência:
const axios = require('axios');
const { RateLimiter } = require('limiter');
class ProxyLoadBalancer {
constructor(proxies, algorithm = 'round_robin', rateLimit = 10) {
this.proxies = proxies;
this.algorithm = algorithm;
this.currentIndex = 0;
this.connections = new Map();
this.limiters = new Map();
this.metrics = new Map();
// Inicialização dos rate limiters
proxies.forEach(proxy => {
this.limiters.set(proxy.url, new RateLimiter({
tokensPerInterval: rateLimit,
interval: 'second'
}));
this.connections.set(proxy.url, 0);
this.metrics.set(proxy.url, {
total: 0,
success: 0,
failed: 0,
responseTimes: []
});
});
}
getNextProxy() {
if (this.algorithm === 'round_robin') {
const proxy = this.proxies[this.currentIndex].url;
this.currentIndex = (this.currentIndex + 1) % this.proxies.length;
return proxy;
} else if (this.algorithm === 'least_connections') {
let minConn = Infinity;
let selectedProxy = null;
this.connections.forEach((count, proxy) => {
if (count < minConn) {
minConn = count;
selectedProxy = proxy;
}
});
return selectedProxy;
}
}
async makeRequest(url, options = {}) {
const proxyUrl = this.getNextProxy();
const limiter = this.limiters.get(proxyUrl);
// Aguardamos disponibilidade do rate limit
await limiter.removeTokens(1);
// Aumentamos o contador de conexões
this.connections.set(proxyUrl, this.connections.get(proxyUrl) + 1);
try {
const startTime = Date.now();
const response = await axios({
url,
proxy: this.parseProxyUrl(proxyUrl),
timeout: options.timeout || 30000,
...options
});
const responseTime = (Date.now() - startTime) / 1000;
this.recordMetrics(proxyUrl, responseTime, true);
return response;
} catch (error) {
this.recordMetrics(proxyUrl, 0, false);
throw error;
} finally {
this.connections.set(proxyUrl, this.connections.get(proxyUrl) - 1);
}
}
parseProxyUrl(proxyUrl) {
const url = new URL(proxyUrl);
return {
host: url.hostname,
port: parseInt(url.port)
};
}
recordMetrics(proxyUrl, responseTime, success) {
const m = this.metrics.get(proxyUrl);
m.total++;
if (success) {
m.success++;
m.responseTimes.push(responseTime);
if (m.responseTimes.length > 1000) {
m.responseTimes.shift();
}
} else {
m.failed++;
}
}
getStats() {
const stats = [];
this.proxies.forEach(proxy => {
const m = this.metrics.get(proxy.url);
const avgResponseTime = m.responseTimes.length > 0
? m.responseTimes.reduce((a, b) => a + b, 0) / m.responseTimes.length
: 0;
const successRate = m.total > 0 ? (m.success / m.total * 100) : 0;
stats.push({
proxy: proxy.url,
totalRequests: m.total,
successRate: successRate.toFixed(2),
avgResponseTime: avgResponseTime.toFixed(3),
activeConnections: this.connections.get(proxy.url)
});
});
return stats;
}
}
// Uso
const proxies = [
{ url: 'http://proxy1.example.com:8080', weight: 5 },
{ url: 'http://proxy2.example.com:8080', weight: 5 }
];
const balancer = new ProxyLoadBalancer(proxies, 'round_robin', 60);
async function parseProducts() {
const urls = Array.from({ length: 1000 }, (_, i) =>
`https://www.wildberries.ru/catalog/${i}/detail.aspx`
);
for (const url of urls) {
try {
const response = await balancer.makeRequest(url);
console.log(`Success: ${url}`);
} catch (error) {
console.error(`Error: ${url} - ${error.message}`);
}
}
console.log('\n=== Statistics ===');
console.log(balancer.getStats());
}
parseProducts();
Otimização para tarefas específicas: parsing, API, automação
Diferentes tarefas requerem diferentes estratégias de balanceamento de carga. Para parsing de marketplaces (Wildberries, Ozon, Avito), a estratégia ideal é com rotação por número de requisições e distribuição geográfica. Use proxies residenciais de diferentes cidades da Rússia, troque proxies a cada 50-100 requisições, adicione atrasos aleatórios entre requisições (1-3 segundos) para imitar comportamento humano.
Para trabalhar com APIs de redes sociais (Instagram, Facebook, VK), é crítica a estabilidade do endereço IP dentro de uma sessão. Use o algoritmo IP Hash ou sticky sessions: todas as requisições de uma conta devem passar pelo mesmo proxy. Isso previne mudança suspeita de geolocalização, que pode causar bloqueio da conta. São recomendados proxies móveis, pois as redes sociais confiam mais em IPs móveis do que em residenciais ou data centers.
# Exemplo de sticky sessions para Instagram
class StickySessionBalancer:
def __init__(self, proxies):
self.proxies = proxies
self.session_map = {} # {account_id: proxy_url}
self.proxy_usage = defaultdict(int)
def get_proxy_for_account(self, account_id):
"""Retorna proxy fixo para a conta"""
if account_id in self.session_map:
return self.session_map[account_id]
# Escolhemos o proxy menos carregado
proxy = min(self.proxies, key=lambda p: self.proxy_usage[p])
self.session_map[account_id] = proxy
self.proxy_usage[proxy] += 1
return proxy
def release_account(self, account_id):
"""Libera o proxy ao concluir o trabalho com a conta"""
if account_id in self.session_map:
proxy = self.session_map[account_id]
self.proxy_usage[proxy] -= 1
del self.session_map[account_id]
# Uso para Instagram
balancer = StickySessionBalancer([
"http://mobile-proxy1.com:8080",
"http://mobile-proxy2.com:8080",
"http://mobile-proxy3.com:8080"
])
# Trabalho com múltiplas contas
accounts = ["account1", "account2", "account3", "account4", "account5"]
for account_id in accounts:
proxy = balancer.get_proxy_for_account(account_id)
print(f"Account {account_id} → {proxy}")
# Executar ações com a conta através deste proxy
# login(account_id, proxy)
# post_content(account_id, proxy)
# logout(account_id, proxy)
# Liberar proxy após conclusão
balancer.release_account(account_id)
Para automação de ações em massa (registro de contas, envio de mensagens, coleta de dados de perfis), use uma combinação de estratégias. Distribua contas entre proxies usando sticky sessions, mas adicione rotação periódica (troca de proxy a cada 24 horas) para evitar padrões suspeitos. Implemente delays aleatórios entre ações (5-15 segundos) e varie a sequência de ações para cada conta.
Para trabalhar com APIs públicas (Google Maps, OpenWeatherMap, APIs de criptomoedas), a prioridade é maximizar a taxa de requisições dentro dos limites da API. Use algoritmo Least Connections para distribuir uniformemente a carga e implemente rate limiting preciso para cada proxy. Monitore códigos de resposta 429 (Too Many Requests) e ajuste automaticamente os limites.
Otimização para e-commerce: Ao monitorar preços em vários marketplaces simultaneamente (Wildberries, Ozon, AliExpress), crie pools de proxies separados para cada plataforma. Isso permite ajustar individualmente os parâmetros de rate limiting e rotação para cada site, maximizando a eficiência da coleta de dados.
Para scraping de mecanismos de busca (Google, Yandex, Bing), use proxies residenciais com rotação frequente (a cada 10-20 requisições) e adicione User-Agent aleatório para cada requisição. Implemente delays adaptativos: se detectar CAPTCHA ou bloqueio, aumente automaticamente o delay entre requisições e troque o proxy.
Conclusão
O balanceamento de carga através de proxies é um componente fundamental de qualquer sistema de alta carga para parsing, automação de API ou trabalho com múltiplas contas. A escolha correta do algoritmo de balanceamento, implementação de rate limiting, monitoramento constante de métricas e otimização para tarefas específicas permitem alcançar alta performance, confiabilidade e proteção contra bloqueios.
Os principais princípios de um sistema eficaz: distribuição uniforme de carga entre proxies, verificação automática de operacionalidade (health checks), controle rigoroso de frequência de requisições (rate limiting), coleta e análise de métricas de desempenho, escalabilidade automática dependendo da carga. A implementação desses princípios permite construir sistemas robustos capazes de processar milhões de requisições por dia sem bloqueios e falhas.
Lembre-se de que diferentes tarefas requerem diferentes abordagens: parsing de marketplaces — rotação frequente e distribuição geográfica, trabalho com redes sociais — sticky sessions e proxies móveis, APIs públicas — maximização de taxa de requisições dentro dos limites. Adapte a arquitetura do seu sistema às necessidades específicas do negócio e monitore constantemente as métricas para otimização oportuna.
Para implementação de um sistema de balanceamento de carga de nível profissional, considere usar serviços de proxy especializados como ProxyCove, que oferecem pools de proxies prontos com balanceamento integrado, rotação automática e monitoramento de qualidade. Isso permite focar na lógica de negócios em vez de gerenciar a infraestrutura de proxies.