Proxy lento: 7 razones de la caída de velocidad y métodos de aceleración
La velocidad de la conexión proxy afecta directamente la eficacia del scraping, la automatización y cualquier tarea relacionada con solicitudes masivas. Cuando un proxy funciona lentamente, esto lleva a un aumento en el tiempo de ejecución de los scripts, timeouts y pérdida de datos. En este artículo, analizaremos las razones técnicas de la baja velocidad y mostraremos formas concretas de optimización con ejemplos de código y resultados de pruebas.
Distancia geográfica del servidor
La distancia física entre su servidor, el proxy y el recurso objetivo es un factor principal de latencia. Cada nodo adicional en la cadena agrega milisegundos que se acumulan en solicitudes masivas.
Un esquema típico de solicitud a través de un proxy es el siguiente: su servidor → servidor proxy → sitio objetivo → servidor proxy → su servidor. Si su scraper está en Alemania, el proxy en EE. UU. y el sitio objetivo en Japón, los datos recorren decenas de miles de kilómetros.
Ejemplo práctico: La prueba de 1000 solicitudes a un sitio europeo mostró una diferencia en el tiempo de respuesta promedio: a través de un proxy en Europa — 180 ms, a través de un proxy en Asia — 520 ms. La diferencia de 340 ms por cada solicitud da 340 segundos (5.6 minutos) en 1000 solicitudes.
Solución: Elija proxies geográficamente cercanos al recurso objetivo. Si está scrapeando sitios rusos, use proxies con IP rusas. Para trabajar con servicios globales (Google, Amazon), los proxies en EE. UU. o Europa Occidental son óptimos, donde se encuentran los principales centros de datos.
Para proxies residenciales, preste atención a la posibilidad de elegir una ciudad o región específica, no solo un país. La diferencia en el ping entre proxies de Moscú y Vladivostok al acceder a un servidor de Moscú puede alcanzar los 150-200 ms.
Influencia del protocolo en la velocidad de transferencia de datos
La elección del protocolo de proxy afecta significativamente la velocidad. Las opciones principales son: HTTP/HTTPS, SOCKS4, SOCKS5. Cada uno tiene sus propias características de procesamiento de datos y costos.
| Protocolo | Velocidad | Costos | Aplicación |
|---|---|---|---|
| HTTP | Alta | Mínimos | Web scraping, API |
| HTTPS | Media | +15-25% en SSL | Conexiones seguras |
| SOCKS4 | Alta | Bajos | Tráfico TCP |
| SOCKS5 | Media-Alta | +5-10% en autenticación | Tráfico universal, UDP |
Los proxies HTTP son óptimos para el web scraping, ya que funcionan a nivel de aplicación y pueden almacenar en caché los datos. SOCKS5 es más versátil, pero agrega una capa adicional de procesamiento. Para el simple scraping de HTML, la diferencia de velocidad entre HTTP y SOCKS5 puede ser del 10-15%.
Ejemplo de configuración en Python (requests):
import requests
# Proxy HTTP - más rápido para solicitudes web
proxies_http = {
'http': 'http://user:pass@proxy.example.com:8080',
'https': 'http://user:pass@proxy.example.com:8080'
}
# SOCKS5 - más versátil, pero más lento
proxies_socks = {
'http': 'socks5://user:pass@proxy.example.com:1080',
'https': 'socks5://user:pass@proxy.example.com:1080'
}
# Para web scraping, use HTTP
response = requests.get('https://example.com', proxies=proxies_http, timeout=10)
Si su proveedor ofrece ambas opciones, pruébelas en tareas reales. Para trabajar con proxies de centros de datos, el protocolo HTTP generalmente muestra una velocidad un 12-18% superior en comparación con SOCKS5 bajo la misma carga.
Sobrecarga del servidor proxy y grupos de IP
Cuando un servidor proxy maneja demasiadas conexiones simultáneas, la velocidad disminuye debido a las limitaciones de ancho de banda y recursos computacionales. Esto es especialmente crítico para proxies compartidos, donde una IP es utilizada por decenas de clientes.
Una imagen típica de sobrecarga: al inicio del script, la velocidad es normal (50-100 solicitudes por minuto), luego cae bruscamente a 10-15 solicitudes. Esto ocurre cuando el servidor alcanza el límite de conexiones abiertas o de ancho de banda.
Señales de sobrecarga: aumento del tiempo de respuesta en un 200% o más, timeouts periódicos, errores "Connection reset by peer", velocidad inestable con saltos bruscos.
Soluciones:
- Utilice un grupo de proxies en lugar de una sola IP. La rotación entre 10-20 proxies distribuye la carga y reduce la probabilidad de bloqueo.
- Limite el número de conexiones simultáneas a través de un proxy (se recomienda no más de 5-10 hilos paralelos).
- Para tareas de alta carga, elija proxies privados, donde los recursos no se comparten con otros usuarios.
- Monitoree la velocidad en tiempo real y excluya automáticamente los proxies lentos de la rotación.
Ejemplo de implementación de un grupo con monitoreo de velocidad:
import time
import requests
from collections import deque
class ProxyPool:
def __init__(self, proxies, max_response_time=5.0):
self.proxies = deque(proxies)
self.max_response_time = max_response_time
self.stats = {p: {'total': 0, 'slow': 0} for p in proxies}
def get_proxy(self):
"""Obtener el siguiente proxy del grupo"""
proxy = self.proxies[0]
self.proxies.rotate(-1) # Mover al final
return proxy
def test_and_remove_slow(self, url='http://httpbin.org/ip'):
"""Probar y eliminar proxies lentos"""
for proxy in list(self.proxies):
try:
start = time.time()
requests.get(url, proxies={'http': proxy}, timeout=10)
response_time = time.time() - start
self.stats[proxy]['total'] += 1
if response_time > self.max_response_time:
self.stats[proxy]['slow'] += 1
# Eliminar si más del 50% de las solicitudes son lentas
slow_ratio = self.stats[proxy]['slow'] / self.stats[proxy]['total']
if slow_ratio > 0.5 and self.stats[proxy]['total'] > 10:
self.proxies.remove(proxy)
print(f"Proxy lento eliminado: {proxy}")
except:
self.proxies.remove(proxy)
# Uso
proxies = [
'http://proxy1.example.com:8080',
'http://proxy2.example.com:8080',
'http://proxy3.example.com:8080'
]
pool = ProxyPool(proxies, max_response_time=3.0)
pool.test_and_remove_slow()
# Trabajando con el grupo
for i in range(100):
proxy = pool.get_proxy()
# Realizar solicitud a través del proxy
Configuraciones de conexión y timeouts
Los parámetros de conexión mal configurados son una causa común de la aparente lentitud de los proxies. Timeouts demasiado largos hacen que el script espere proxies no disponibles, mientras que timeouts demasiado cortos llevan a la desconexión de conexiones normales.
Parámetros clave que afectan la velocidad:
- Timeout de conexión — tiempo de espera para establecer la conexión. Óptimo: 5-10 segundos para proxies residenciales, 3-5 para proxies de centros de datos.
- Timeout de lectura — tiempo de espera para la respuesta después de establecer la conexión. Depende de la tarea: 10-15 segundos para scraping, 30+ para descargar archivos grandes.
- Keep-Alive — reutilización de conexiones TCP. Ahorra hasta 200-300 ms en cada solicitud posterior al mismo dominio.
- Pooling de conexiones — grupo de conexiones abiertas. Crítico para un alto rendimiento en solicitudes masivas.
Configuración optimizada para requests:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# Crear una sesión con configuraciones optimizadas
session = requests.Session()
# Configuración de reintentos
retry_strategy = Retry(
total=3, # Máximo 3 intentos
backoff_factor=0.5, # Retraso entre intentos: 0.5, 1, 2 segundos
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"]
)
# Adaptador con pooling de conexiones
adapter = HTTPAdapter(
max_retries=retry_strategy,
pool_connections=10, # Pool para 10 hosts
pool_maxsize=20 # Máximo 20 conexiones
)
session.mount("http://", adapter)
session.mount("https://", adapter)
# Configuración de proxy
session.proxies = {
'http': 'http://user:pass@proxy.example.com:8080',
'https': 'http://user:pass@proxy.example.com:8080'
}
# Solicitud con timeouts óptimos
# (connection_timeout, read_timeout)
response = session.get(
'https://example.com',
timeout=(5, 15), # 5 seg para conexión, 15 para lectura
headers={'Connection': 'keep-alive'} # Reutilización de conexión
)
Usar sesiones con Keep-Alive al scrapeo de 1000 páginas de un mismo sitio acelera el trabajo en un 30-40% en comparación con crear una nueva conexión para cada solicitud. El ahorro de tiempo en el establecimiento de la conexión TCP y el handshake SSL es crítico en operaciones masivas.
Costos de cifrado y SSL/TLS
Las conexiones HTTPS requieren recursos computacionales adicionales para cifrar/descifrar datos y realizar el handshake SSL/TLS. Al trabajar a través de un proxy, esto ocurre dos veces: entre usted y el proxy, y entre el proxy y el servidor objetivo.
Costos típicos de SSL/TLS:
- Handshake inicial: 150-300 ms (depende del algoritmo y la distancia)
- Cifrado/descifrado de datos: +10-20% al tiempo de transmisión
- Carga adicional en la CPU del servidor proxy con alto tráfico
Métodos de optimización:
1. Utilice TLS Session Resumption
Permite reutilizar los parámetros de la sesión SSL y omitir el handshake completo. Ahorra hasta 200 ms en cada conexión posterior.
En Python, esto funciona automáticamente al usar requests.Session(), pero asegúrese de no crear una nueva sesión para cada solicitud.
2. Prefiera TLS 1.3
TLS 1.3 requiere solo un round-trip para el handshake en lugar de dos en TLS 1.2. Esto reduce el tiempo de establecimiento de la conexión en un 30-50%.
Asegúrese de que su biblioteca (OpenSSL, urllib3) soporte TLS 1.3 y que no esté deshabilitada en la configuración.
3. Para tareas internas, considere HTTP
Si está scrapeando datos públicos que no contienen información confidencial y el sitio está disponible por HTTP, use una conexión no cifrada. Esto dará un aumento de velocidad del 15-25%.
Al trabajar con proxies móviles, donde el canal de comunicación puede ser más lento, los costos de SSL se vuelven aún más evidentes. En pruebas, la diferencia entre solicitudes HTTP y HTTPS a través de proxies 4G fue de 280 ms en promedio.
Resolución DNS y caché
Cada solicitud a un nuevo dominio requiere resolución DNS — la conversión del nombre de dominio a una dirección IP. Sin caché, esto agrega 20-100 ms a cada solicitud, y con un servidor DNS lento, la latencia puede alcanzar los 500+ ms.
Cuando trabaja a través de un proxy, las solicitudes DNS pueden realizarse en tres lugares:
- De su lado (el cliente resuelve el dominio y pasa la IP al proxy)
- En el servidor proxy (SOCKS5, HTTP CONNECT — el proxy recibe el dominio y lo resuelve él mismo)
- En el servidor objetivo (raro, en configuraciones específicas)
Para proxies SOCKS5, la resolución DNS generalmente ocurre en el lado del servidor proxy, lo que puede ser más lento si el proveedor de proxy tiene malos servidores DNS. Los proxies HTTP suelen resolver en el lado del cliente.
Métodos para acelerar DNS:
import socket
from functools import lru_cache
# Caché de resolución DNS en el lado del cliente
@lru_cache(maxsize=256)
def cached_resolve(hostname):
"""Caché de resultados de solicitudes DNS"""
try:
return socket.gethostbyname(hostname)
except socket.gaierror:
return None
# Uso
hostname = 'example.com'
ip = cached_resolve(hostname)
if ip:
# Usar IP directamente en solicitudes
url = f'http://{ip}/path'
headers = {'Host': hostname} # Especificar el host original en el encabezado
Un enfoque alternativo es utilizar servidores DNS públicos rápidos a nivel del sistema:
- Google DNS: 8.8.8.8, 8.8.4.4
- Cloudflare DNS: 1.1.1.1, 1.0.0.1
- Quad9: 9.9.9.9
En Linux, la configuración se realiza a través de /etc/resolv.conf:
nameserver 1.1.1.1
nameserver 8.8.8.8
options timeout:2 attempts:2
Para scripts de Python con un gran número de dominios, se recomienda precalentar la caché DNS:
import concurrent.futures
import socket
def warmup_dns_cache(domains):
"""Pre-resolver una lista de dominios"""
def resolve(domain):
try:
socket.gethostbyname(domain)
except:
pass
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
executor.map(resolve, domains)
# Lista de dominios para scraping
domains = ['site1.com', 'site2.com', 'site3.com']
warmup_dns_cache(domains)
# Ahora DNS ya está en caché, las solicitudes serán más rápidas
Calidad de la infraestructura del proveedor
La velocidad del proxy depende directamente de la calidad del hardware y los canales de comunicación del proveedor. Los proxies baratos a menudo funcionan en servidores sobrecargados con interfaces de red lentas y hardware obsoleto.
Parámetros críticos de infraestructura:
| Parámetro | Malo | Bueno | Influencia en la velocidad |
|---|---|---|---|
| Ancho de banda del canal | 100 Mbit/s | 1+ Gbit/s | Crítico al cargar archivos |
| Procesador del servidor | 2-4 núcleos | 8+ núcleos | Afecta el procesamiento de SSL/TLS |
| RAM | 4-8 GB | 16+ GB | Caché y buffering |
| Uptime | <95% | 99%+ | Estabilidad de las conexiones |
| Enrutamiento | Estándar | BGP optimizado | Latencia y pérdida de paquetes |
Los proveedores con infraestructura propia (no revendedores) suelen ofrecer velocidades consistentemente altas. Controlan toda la pila: desde el hardware hasta la configuración del equipo de red.
Señales de una infraestructura de calidad:
- Velocidad estable durante el día (desviaciones no superiores al 15-20% del promedio)
- Bajo jitter (variación de latencia) — menos de 10 ms
- Mínimas pérdidas de paquetes (<0.1%)
- Rápida respuesta del soporte técnico ante problemas (importante para tareas empresariales)
- Información transparente sobre la ubicación de los servidores y características de los canales
Para tareas críticas, se recomienda probar los proxies en condiciones lo más cercanas posible a las reales. Adquiera acceso de prueba por 1-3 días y ejecute scripts reales monitoreando todas las métricas.
Metodología de prueba de velocidad del proxy
La prueba adecuada ayuda a identificar cuellos de botella y comparar objetivamente diferentes proveedores. Una simple prueba de velocidad no es suficiente; es necesario medir parámetros importantes para sus tareas.
Métricas clave para medir:
- Latencia — tiempo de ida y vuelta del paquete. Crítico para tareas con muchas solicitudes pequeñas.
- Throughput — volumen de datos por unidad de tiempo. Importante para la carga de archivos e imágenes.
- Tiempo de conexión — tiempo para establecer la conexión. Muestra la eficiencia para solicitudes únicas.
- Tasa de éxito — porcentaje de solicitudes exitosas. Por debajo del 95% es un mal indicador.
- Jitter — variación de latencia. Un jitter alto (>50 ms) indica inestabilidad en el canal.
Script integral para pruebas:
import time
import requests
import statistics
from concurrent.futures import ThreadPoolExecutor, as_completed
def test_proxy_performance(proxy, test_url='https://httpbin.org/get', requests_count=50):
"""
Prueba integral del proxy
Args:
proxy: URL del proxy
test_url: URL para prueba
requests_count: Número de solicitudes de prueba
Returns:
dict con métricas
"""
results = {
'latencies': [],
'connection_times': [],
'total_times': [],
'successes': 0,
'failures': 0,
'errors': []
}
session = requests.Session()
session.proxies = {'http': proxy, 'https': proxy}
def single_request():
try:
start = time.time()
response = session.get(
test_url,
timeout=(5, 15),
headers={'Connection': 'keep-alive'}
)
total_time = time.time() - start
if response.status_code == 200:
results['successes'] += 1
results['total_times'].append(total_time)
# Estimación aproximada de latencia
results['latencies'].append(total_time / 2)
else:
results['failures'] += 1
except Exception as e:
results['failures'] += 1
results['errors'].append(str(e))
# Ejecución paralela de solicitudes
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(single_request) for _ in range(requests_count)]
for future in as_completed(futures):
future.result()
# Cálculo de estadísticas
if results['total_times']:
metrics = {
'proxy': proxy,
'total_requests': requests_count,
'success_rate': (results['successes'] / requests_count) * 100,
'avg_response_time': statistics.mean(results['total_times']),
'median_response_time': statistics.median(results['total_times']),
'min_response_time': min(results['total_times']),
'max_response_time': max(results['total_times']),
'stdev_response_time': statistics.stdev(results['total_times']) if len(results['total_times']) > 1 else 0,
'jitter': statistics.stdev(results['latencies']) if len(results['latencies']) > 1 else 0,
'failures': results['failures']
}
return metrics
else:
return {'proxy': proxy, 'error': 'Todas las solicitudes fallaron'}
# Prueba
proxy = 'http://user:pass@proxy.example.com:8080'
metrics = test_proxy_performance(proxy, requests_count=100)
print(f"Proxy: {metrics['proxy']}")
print(f"Tasa de éxito: {metrics['success_rate']:.1f}%")
print(f"Tiempo de respuesta promedio: {metrics['avg_response_time']*1000:.0f} ms")
print(f"Mediana: {metrics['median_response_time']*1000:.0f} ms")
print(f"Jitter: {metrics['jitter']*1000:.0f} ms")
print(f"Desviación estándar: {metrics['stdev_response_time']*1000:.0f} ms")
Para obtener resultados más precisos, pruebe en diferentes momentos del día (mañana, tarde, noche) y en diferentes sitios objetivo. La velocidad puede variar significativamente según la geografía y la carga en la red.
Consejo: Cree una línea base (baseline) — pruebe una conexión directa sin proxy. Esto proporcionará un punto de referencia para evaluar los costos del proxy. Los costos normales: 50-150 ms para proxies de calidad.
Optimización integral: lista de verificación
La aplicación de todos los métodos descritos en conjunto produce un efecto acumulativo. Aquí hay un plan paso a paso para optimizar la velocidad de trabajo a través de proxies:
Paso 1: Selección y configuración del proxy
- Elija proxies geográficamente cercanos a los recursos objetivo
- Para web scraping, use el protocolo HTTP en lugar de SOCKS5
- Prefiera proxies privados para tareas de alta carga
- Asegúrese de que el proveedor soporte TLS 1.3
Paso 2: Optimización del código
- Utilice
requests.Session()con Keep-Alive - Configure el pooling de conexiones (10-20 conexiones)
- Establezca timeouts óptimos: 5-10 seg para conexión, 15-30 para lectura
- Implemente lógica de reintento con backoff exponencial
- Cache la resolución DNS
Paso 3: Gestión del grupo de proxies
- Cree un grupo de 10-50 proxies para rotación
- Limite el número de solicitudes simultáneas a través de un proxy (5-10 hilos)
- Monitoree la velocidad y excluya automáticamente proxies lentos
- Utilice sesiones persistentes para tareas que requieren mantener la IP
Paso 4: Optimización del sistema
- Configure servidores DNS rápidos (1.1.1.1, 8.8.8.8)
- Aumente los límites de archivos abiertos en el SO (ulimit -n 65535)
- Para Linux: optimice los parámetros TCP del núcleo
- Utilice SSD para caché si trabaja con grandes volúmenes de datos
Paso 5: Monitoreo y pruebas
- Pruebe regularmente la velocidad del proxy (mínimo una vez a la semana)
- Registre métricas: tiempo de respuesta, tasa de éxito, errores
- Compare el rendimiento de diferentes proveedores
- Configure alertas si la velocidad cae por debajo de un umbral
Ejemplo de configuración optimizada para producción:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from collections import deque
import time
class OptimizedProxyPool:
def __init__(self, proxies_list):
self.proxies = deque(proxies_list)
self.session = self._create_optimized_session()
self.stats = {p: {'requests': 0, 'avg_time': 0} for p in proxies_list}
def _create_optimized_session(self):
"""Crear una sesión optimizada"""
session = requests.Session()
# Estrategia de reintentos
retry = Retry(
total=3,
backoff_factor=0.3,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST", "PUT"]
)
# Adaptador con pooling de conexiones
adapter = HTTPAdapter(
max_retries=retry,
pool_connections=20,
pool_maxsize=50,
pool_block=False
)
session.mount("http://", adapter)
session.mount("https://", adapter)
# Encabezados de Keep-Alive
session.headers.update({
'Connection': 'keep-alive',
'Keep-Alive': 'timeout=60, max=100'
})
return session
def get_best_proxy(self):
"""Obtener el proxy con mejor rendimiento"""
# Ordenar por velocidad promedio
sorted_proxies = sorted(
self.stats.items(),
key=lambda x: x[1]['avg_time'] if x[1]['requests'] > 0 else float('inf')
)
return sorted_proxies[0][0] if sorted_proxies else self.proxies[0]
def request(self, url, method='GET', **kwargs):
"""Realizar una solicitud a través del proxy óptimo"""
proxy = self.get_best_proxy()
self.session.proxies = {'http': proxy, 'https': proxy}
start = time.time()
try:
response = self.session.request(
method,
url,
timeout=(5, 15), # conexión, lectura
**kwargs
)
# Actualizar estadísticas
elapsed = time.time() - start
stats = self.stats[proxy]
stats['avg_time'] = (
(stats['avg_time'] * stats['requests'] + elapsed) /
(stats['requests'] + 1)
)
stats['requests'] += 1
return response
except Exception as e:
# En caso de error, mover el proxy al final de la cola
self.proxies.remove(proxy)
self.proxies.append(proxy)
raise e
# Uso
proxies = [
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
'http://user:pass@proxy3.example.com:8080'
]
pool = OptimizedProxyPool(proxies)
# Realización de solicitudes
for url in ['https://example.com', 'https://example.org']:
try:
response = pool.request(url)
print(f"Éxito: {url}, estado: {response.status_code}")
except Exception as e:
print(f"Error: {url}, {e}")
La aplicación de esta lista de verificación permite aumentar la velocidad de trabajo a través de proxies de 2 a 3 veces en comparación con configuraciones básicas. En proyectos reales de scraping, esto reduce el tiempo de ejecución de tareas de horas a minutos.
Conclusión
El funcionamiento lento de los proxies es un problema solucionable si se comprenden las razones técnicas y se aplican los métodos de optimización correctos. Los factores principales de velocidad son: cercanía geográfica, elección del protocolo, calidad de la infraestructura del proveedor y configuración adecuada del código del cliente.
Un enfoque integral para la optimización incluye la selección cuidadosa de proxies, la configuración adecuada de parámetros y la implementación de prácticas de monitoreo y prueba. Con esto, se puede lograr un rendimiento óptimo en las tareas de scraping y automatización.