Torna al blog

Perché i proxy sono lenti e come velocizzarli

Analisi tecnica dettagliata delle cause della lenta operatività dei server proxy, con soluzioni pratiche, esempi di codice e risultati dei test di vari metodi di ottimizzazione.

📅16 dicembre 2025
```html

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...

```