Proxy lenti: 7 motivi per la diminuzione della velocità e metodi di accelerazione
La velocità della connessione proxy influisce direttamente sull'efficacia del parsing, dell'automazione e di qualsiasi attività legata a richieste di massa. Quando un proxy funziona lentamente, ciò porta a un aumento dei tempi di esecuzione degli script, timeout e perdita di dati. In questo articolo analizzeremo le ragioni tecniche della bassa velocità e mostreremo modi specifici per ottimizzare con esempi di codice e risultati di test.
Distanza geografica del server
La distanza fisica tra il tuo server, il proxy e la risorsa target è il principale fattore di latenza. Ogni nodo aggiuntivo nella catena aggiunge millisecondi, che si accumulano durante le richieste di massa.
Uno schema tipico di richiesta attraverso un proxy appare così: il tuo server → server proxy → sito target → server proxy → il tuo server. Se il tuo parser si trova in Germania, il proxy negli Stati Uniti e il sito target in Giappone, i dati percorrono decine di migliaia di chilometri.
Esempio pratico: Il test di 1000 richieste a un sito europeo ha mostrato una differenza nel tempo medio di risposta: attraverso un proxy in Europa — 180 ms, attraverso un proxy in Asia — 520 ms. La differenza di 340 ms per ogni richiesta porta a 340 secondi (5,6 minuti) per 1000 richieste.
Soluzione: Scegli proxy geograficamente vicini alla risorsa target. Se stai estraendo dati da siti russi, utilizza proxy con IP russi. Per lavorare con servizi globali (Google, Amazon), i proxy negli Stati Uniti o in Europa occidentale sono ottimali, dove si trovano i principali data center.
Per proxy residenziali, presta attenzione alla possibilità di scegliere una città o regione specifica, e non solo il paese. La differenza di ping tra proxy da Mosca e Vladivostok quando si accede a un server di Mosca può raggiungere i 150-200 ms.
Influenza del protocollo sulla velocità di trasmissione dei dati
La scelta del protocollo proxy influisce notevolmente sulla velocità. Le opzioni principali sono: HTTP/HTTPS, SOCKS4, SOCKS5. Ognuno ha le proprie caratteristiche di elaborazione dei dati e sovraccarichi.
| Protocollo | Velocità | Sovraccarichi | Applicazione |
|---|---|---|---|
| HTTP | Alta | Minimi | Web scraping, API |
| HTTPS | Media | +15-25% su SSL | Connessioni sicure |
| SOCKS4 | Alta | Bassi | Traffico TCP |
| SOCKS5 | Media-Alta | +5-10% per l'autenticazione | Traffico universale, UDP |
I proxy HTTP sono ottimali per il web scraping, poiché operano a livello applicativo e possono memorizzare i dati nella cache. I SOCKS5 sono più versatili, ma aggiungono uno strato di elaborazione aggiuntivo. Per il semplice parsing HTML, la differenza di velocità tra HTTP e SOCKS5 può essere del 10-15%.
Esempio di configurazione in Python (requests):
import requests
# Proxy HTTP - più veloce per le richieste web
proxies_http = {
'http': 'http://user:pass@proxy.example.com:8080',
'https': 'http://user:pass@proxy.example.com:8080'
}
# SOCKS5 - più versatile, ma più lento
proxies_socks = {
'http': 'socks5://user:pass@proxy.example.com:1080',
'https': 'socks5://user:pass@proxy.example.com:1080'
}
# Per il web scraping utilizza HTTP
response = requests.get('https://example.com', proxies=proxies_http, timeout=10)
Se il tuo provider offre entrambe le opzioni, testale su compiti reali. Per lavorare con proxy di data center, il protocollo HTTP di solito mostra velocità superiori del 12-18% rispetto a SOCKS5 con carico identico.
Sovraccarico del server proxy e pool di IP
Quando un server proxy gestisce troppe connessioni simultanee, la velocità diminuisce a causa delle limitazioni della larghezza di banda e delle risorse computazionali. Questo è particolarmente critico per i proxy condivisi, dove un IP è utilizzato da decine di clienti.
Un quadro tipico di sovraccarico: all'inizio dell'esecuzione dello script, la velocità è normale (50-100 richieste al minuto), poi scende bruscamente a 10-15 richieste. Questo accade quando il server raggiunge il limite di connessioni aperte o di larghezza di banda.
Segni di sovraccarico: aumento del tempo di risposta del 200%+, timeout periodici, errori "Connection reset by peer", velocità instabile con improvvisi picchi.
Soluzioni:
- Utilizza un pool di proxy invece di un singolo IP. La rotazione tra 10-20 proxy distribuisce il carico e riduce la probabilità di blocchi.
- Limita il numero di connessioni simultanee attraverso un proxy (si raccomanda non più di 5-10 thread paralleli).
- Per compiti ad alto carico, scegli proxy privati (dedicati), dove le risorse non sono condivise con altri utenti.
- Monitora la velocità in tempo reale ed escludi automaticamente i proxy lenti dalla rotazione.
Esempio di implementazione di un pool con monitoraggio della velocità:
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):
"""Ottieni il prossimo proxy dal pool"""
proxy = self.proxies[0]
self.proxies.rotate(-1) # Sposta in fondo
return proxy
def test_and_remove_slow(self, url='http://httpbin.org/ip'):
"""Testa e rimuovi proxy lenti"""
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
# Rimuovi se oltre il 50% delle richieste sono lente
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"Rimosso proxy lento: {proxy}")
except:
self.proxies.remove(proxy)
# Utilizzo
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()
# Lavorare con il pool
for i in range(100):
proxy = pool.get_proxy()
# Esegui la richiesta attraverso il proxy
Impostazioni di connessione e timeout
Parametri di connessione configurati in modo errato sono una causa comune della apparente lentezza dei proxy. Timeout troppo lunghi costringono lo script ad attendere proxy non disponibili, timeout troppo brevi portano a interruzioni di connessioni normali.
Parametri chiave che influenzano la velocità:
- Connection timeout — tempo di attesa per stabilire la connessione. Ottimale: 5-10 secondi per proxy residenziali, 3-5 per proxy di data center.
- Read timeout — tempo di attesa per la risposta dopo aver stabilito la connessione. Dipende dal compito: 10-15 secondi per parsing, 30+ per il download di file di grandi dimensioni.
- Keep-Alive — riutilizzo delle connessioni TCP. Risparmia fino a 200-300 ms su ogni richiesta successiva allo stesso dominio.
- Connection pooling — pool di connessioni aperte. Critico per alte prestazioni durante richieste di massa.
Configurazione ottimizzata per requests:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# Creare una sessione con impostazioni ottimizzate
session = requests.Session()
# Impostazione delle ripetizioni
retry_strategy = Retry(
total=3, # Massimo 3 tentativi
backoff_factor=0.5, # Ritardo tra i tentativi: 0.5, 1, 2 secondi
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"]
)
# Adattatore con pooling di connessioni
adapter = HTTPAdapter(
max_retries=retry_strategy,
pool_connections=10, # Pool per 10 host
pool_maxsize=20 # Massimo 20 connessioni
)
session.mount("http://", adapter)
session.mount("https://", adapter)
# Impostazione del proxy
session.proxies = {
'http': 'http://user:pass@proxy.example.com:8080',
'https': 'http://user:pass@proxy.example.com:8080'
}
# Richiesta con timeout ottimali
# (connection_timeout, read_timeout)
response = session.get(
'https://example.com',
timeout=(5, 15), # 5 sec per connessione, 15 per lettura
headers={'Connection': 'keep-alive'} # Riutilizzo della connessione
)
L'uso di sessioni con Keep-Alive durante il parsing di 1000 pagine di un sito accelera il lavoro del 30-40% rispetto alla creazione di una nuova connessione per ogni richiesta. Il risparmio di tempo per stabilire una connessione TCP e un handshake SSL è critico durante operazioni di massa.
Crittografia e sovraccarichi SSL/TLS
Le connessioni HTTPS richiedono risorse computazionali aggiuntive per la crittografia/decrittografia dei dati e per l'esecuzione dell'handshake SSL/TLS. Quando si lavora attraverso un proxy, questo avviene due volte: tra te e il proxy, tra il proxy e il server target.
Tipici sovraccarichi SSL/TLS:
- Handshake iniziale: 150-300 ms (dipende dall'algoritmo e dalla distanza)
- Crittografia/decrittografia dei dati: +10-20% al tempo di trasmissione
- Carico aggiuntivo sulla CPU del server proxy durante il traffico elevato
Metodi di ottimizzazione:
1. Utilizza TLS Session Resumption
Permette di riutilizzare i parametri della sessione SSL e saltare l'handshake completo. Risparmio fino a 200 ms su ogni connessione successiva.
In Python funziona automaticamente utilizzando requests.Session(), ma assicurati di non creare una nuova sessione per ogni richiesta.
2. Preferisci TLS 1.3
TLS 1.3 richiede solo un round-trip per l'handshake invece di due in TLS 1.2. Questo riduce il tempo di stabilizzazione della connessione del 30-50%.
Assicurati che la tua libreria (OpenSSL, urllib3) supporti TLS 1.3 e che non sia disabilitato nelle impostazioni.
3. Per compiti interni considera HTTP
Se stai estraendo dati pubblici che non contengono informazioni riservate e il sito è accessibile tramite HTTP, utilizza una connessione non crittografata. Questo porterà a un aumento della velocità del 15-25%.
Quando si lavora con proxy mobili, dove il canale di comunicazione può essere più lento, i sovraccarichi SSL diventano ancora più evidenti. Nei test, la differenza tra richieste HTTP e HTTPS attraverso proxy 4G è stata in media di 280 ms.
Risoluzione DNS e caching
Ogni richiesta a un nuovo dominio richiede una risoluzione DNS — la conversione del nome di dominio in un indirizzo IP. Senza caching, questo aggiunge 20-100 ms a ogni richiesta, e con un server DNS lento, la latenza può raggiungere 500+ ms.
Quando lavori attraverso un proxy, le richieste DNS possono essere eseguite in tre luoghi:
- Dalla tua parte (il client risolve il dominio e passa l'IP al proxy)
- Sul server proxy (SOCKS5, HTTP CONNECT — il proxy riceve il dominio e lo risolve da solo)
- Sul server target (raramente, in configurazioni specifiche)
Per i proxy SOCKS5, la risoluzione DNS avviene di solito sul lato del server proxy, il che può essere più lento se il provider del proxy ha server DNS scadenti. I proxy HTTP risolvono più frequentemente sul lato client.
Metodi per accelerare DNS:
import socket
from functools import lru_cache
# Caching della risoluzione DNS sul lato client
@lru_cache(maxsize=256)
def cached_resolve(hostname):
"""Cache dei risultati delle richieste DNS"""
try:
return socket.gethostbyname(hostname)
except socket.gaierror:
return None
# Utilizzo
hostname = 'example.com'
ip = cached_resolve(hostname)
if ip:
# Utilizzare l'IP direttamente nelle richieste
url = f'http://{ip}/path'
headers = {'Host': hostname} # Specificare l'host originale nell'intestazione
Un approccio alternativo è utilizzare server DNS pubblici veloci a livello di 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
In Linux, la configurazione viene eseguita tramite /etc/resolv.conf:
nameserver 1.1.1.1
nameserver 8.8.8.8
options timeout:2 attempts:2
Per script Python con un gran numero di domini, si consiglia di riscaldare preventivamente la cache DNS:
import concurrent.futures
import socket
def warmup_dns_cache(domains):
"""Risolvi preventivamente un elenco di domini"""
def resolve(domain):
try:
socket.gethostbyname(domain)
except:
pass
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
executor.map(resolve, domains)
# Elenco di domini da analizzare
domains = ['site1.com', 'site2.com', 'site3.com']
warmup_dns_cache(domains)
# Ora il DNS è già nella cache, le richieste saranno più veloci
Qualità dell'infrastruttura del provider
La velocità del proxy dipende direttamente dalla qualità dell'hardware e dei canali di comunicazione del provider. I proxy economici spesso funzionano su server sovraccarichi con interfacce di rete lente e hardware obsoleto.
Parametri critici dell'infrastruttura:
| Parametro | Scarso | Buono | Influenza sulla velocità |
|---|---|---|---|
| Larghezza di banda del canale | 100 Mbit/s | 1+ Gbit/s | Critico durante il caricamento di file |
| Processore del server | 2-4 core | 8+ core | Influenza sull'elaborazione SSL/TLS |
| RAM | 4-8 GB | 16+ GB | Caching e buffering |
| Uptime | <95% | 99%+ | Stabilità delle connessioni |
| Routing | Standard | Ottimizzato BGP | Latenza e perdita di pacchetti |
I provider con infrastruttura propria (non rivenditori) di solito garantiscono velocità costantemente elevate. Controllano l'intero stack: dall'hardware alle impostazioni delle apparecchiature di rete.
Segni di un'infrastruttura di qualità:
- Velocità stabile durante il giorno (deviazioni non superiori al 15-20% dalla media)
- Basso jitter (variazione della latenza) — meno di 10 ms
- Minime perdite di pacchetti (<0.1%)
- Risposta rapida del supporto tecnico ai problemi (importante per compiti aziendali)
- Informazioni trasparenti sulla posizione dei server e sulle caratteristiche dei canali
Per compiti critici si consiglia di testare i proxy in condizioni il più possibile vicine a quelle operative. Acquista un accesso di prova per 1-3 giorni e esegui script reali monitorando tutte le metriche.
Metodologia di test della velocità del proxy
Un test corretto aiuta a identificare i colli di bottiglia e a confrontare oggettivamente diversi provider. Un semplice speedtest non è sufficiente: è necessario misurare parametri importanti per i tuoi compiti.
Metriche chiave da misurare:
- Latenza — tempo di attraversamento del pacchetto andata e ritorno. Critico per compiti con un gran numero di piccole richieste.
- Throughput — volume di dati in un'unità di tempo. Importante per il caricamento di file, immagini.
- Tempo di connessione — tempo per stabilire la connessione. Mostra l'efficacia per richieste singole.
- Tasso di successo — percentuale di richieste riuscite. Sotto il 95% — un cattivo indicatore.
- Jitter — variazione della latenza. Un alto jitter (>50 ms) indica instabilità del canale.
Script complessivo per il test:
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):
"""
Test complessivo del proxy
Args:
proxy: URL del proxy
test_url: URL per il test
requests_count: Numero di richieste di test
Returns:
dict con metriche
"""
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)
# Stima approssimativa della latenza
results['latencies'].append(total_time / 2)
else:
results['failures'] += 1
except Exception as e:
results['failures'] += 1
results['errors'].append(str(e))
# Esecuzione parallela delle richieste
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()
# Calcolo delle statistiche
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': 'Tutte le richieste sono fallite'}
# Test
proxy = 'http://user:pass@proxy.example.com:8080'
metrics = test_proxy_performance(proxy, requests_count=100)
print(f"Proxy: {metrics['proxy']}")
print(f"Tasso di successo: {metrics['success_rate']:.1f}%")
print(f"Tempo medio di risposta: {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"Deviazione standard: {metrics['stdev_response_time']*1000:.0f} ms")
Per risultati più precisi, testa in diversi momenti della giornata (mattina, pomeriggio, sera) e su diversi siti target. La velocità può variare notevolmente a seconda della geografia e del carico sulla rete.
Consiglio: Crea una linea di base (baseline) — testa una connessione diretta senza proxy. Questo fornirà un punto di riferimento per valutare i sovraccarichi del proxy. I normali sovraccarichi: 50-150 ms per proxy di qualità.
Ottimizzazione complessiva: checklist
L'applicazione di tutti i metodi descritti in combinazione produce un effetto cumulativo. Ecco un piano passo-passo per ottimizzare la velocità di lavoro attraverso i proxy:
Passo 1: Scelta e configurazione del proxy
- Scegli proxy geograficamente vicini alle risorse target
- Per il web scraping utilizza il protocollo HTTP invece di SOCKS5
- Preferisci proxy privati per compiti ad alto carico
- Assicurati che il provider supporti TLS 1.3
Passo 2: Ottimizzazione del codice
- Utilizza
requests.Session()con Keep-Alive - Configura il connection pooling (10-20 connessioni)
- Imposta timeout ottimali: 5-10 sec per connessione, 15-30 per lettura
- Implementa una logica di retry con exponential backoff
- Cache la risoluzione DNS
Passo 3: Gestione del pool di proxy
- Crea un pool di 10-50 proxy per la rotazione
- Limita il numero di richieste simultanee attraverso un proxy (5-10 thread)
- Monitora la velocità e escludi automaticamente i proxy lenti
- Utilizza sessioni sticky per compiti che richiedono la conservazione dell'IP
Passo 4: Ottimizzazione del sistema
- Configura server DNS veloci (1.1.1.1, 8.8.8.8)
- Aumenta i limiti dei file aperti nel sistema operativo (ulimit -n 65535)
- Per Linux: ottimizza i parametri TCP del kernel
- Utilizza SSD per il caching, se lavori con grandi volumi di dati
Passo 5: Monitoraggio e test
- Testa regolarmente la velocità del proxy (almeno una volta a settimana)
- Registra le metriche: tempo di risposta, tasso di successo, errori
- Confronta le prestazioni di diversi provider
- Imposta avvisi quando la velocità scende sotto una soglia
Esempio di configurazione ottimizzata per la produzione:
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):
"""Creare una sessione ottimizzata"""
session = requests.Session()
# Strategia di retry
retry = Retry(
total=3,
backoff_factor=0.3,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST", "PUT"]
)
# Adattatore con connection pooling
adapter = HTTPAdapter(
max_retries=retry,
pool_connections=20,
pool_maxsize=50,
pool_block=False
)
session.mount("http://", adapter)
session.mount("https://", adapter)
# Intestazioni Keep-Alive
session.headers.update({
'Connection': 'keep-alive',
'Keep-Alive': 'timeout=60, max=100'
})
return session
def get_best_proxy(self):
"""Ottieni il proxy con le migliori prestazioni"""
# Ordinamento per velocità media
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):
"""Esegui una richiesta attraverso il proxy ottimale"""
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), # connessione, lettura
**kwargs
)
# Aggiornare le statistiche
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:
# In caso di errore sposta il proxy in fondo alla coda
self.proxies.remove(proxy)
self.proxies.append(proxy)
raise e
# Utilizzo
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)
# Esecuzione delle richieste
for url in ['https://example.com', 'https://example.org']:
try:
response = pool.request(url)
print(f"Successo: {url}, stato: {response.status_code}")
except Exception as e:
print(f"Errore: {url}, {e}")
L'applicazione di questa checklist consente di aumentare la velocità di lavoro attraverso i proxy di 2-3 volte rispetto alle impostazioni di base. Nei progetti reali di scraping, questo riduce il tempo di esecuzione delle attività da ore a minuti.
Conclusione
Il funzionamento lento dei proxy è un problema risolvibile, se si comprendono le ragioni tecniche e si applicano i giusti metodi di ottimizzazione. I principali fattori di velocità sono: vicinanza geografica, scelta del protocollo, qualità dell'infrastruttura del provider e corretta configurazione del codice client.
Un approccio complessivo all'ottimizzazione include...