Il protocollo QUIC è estremamente interessante da osservare, quindi ci piace scriverne. Se le pubblicazioni precedenti su QUIC avevano un carattere più storico (se volete, locale) e tecnico, oggi siamo lieti di pubblicare una traduzione di altro tipo: parleremo dell'applicazione reale del protocollo nel 2019. Parliamo, non di una piccola infrastruttura basata in un garage convenzionale, ma di Uber, che opera in quasi tutto il mondo. Come sono arrivati gli ingegneri dell'azienda a decidere di utilizzare QUIC in produzione, come hanno condotto i test e cosa hanno visto dopo il rollout in produzione – leggete oltre.
Le immagini sono cliccabili. Buona lettura!
Uber è di portata globale, con 600 città in cui è presente, in ognuna delle quali l'applicazione si basa completamente su Internet wireless fornito da oltre 4500 operatori di telefonia mobile. Gli utenti si aspettano che l'app funzioni non solo rapidamente, ma in tempo reale – per garantire ciò, l'app Uber ha bisogno di basse latenze e di una connessione molto affidabile. Purtroppo, ma il pila si comporta male nelle reti wireless dinamiche e soggette a perdite. Abbiamo compreso che, in questo caso, le basse prestazioni sono direttamente correlate alle implementazioni di TCP nei kernel dei sistemi operativi.
Per risolvere il problema, abbiamo applicato , un protocollo moderno con multiplexing dei canali che ci consente di avere un maggiore controllo sulle prestazioni del protocollo di trasporto. Attualmente, il gruppo di lavoro sta standardizzando QUIC come .
Dopo test approfonditi, siamo giunti alla conclusione che l'implementazione di QUIC nella nostra applicazione ridurrà i ritardi 'tail' rispetto a TCP. Abbiamo osservato una diminuzione compresa tra il 10% e il 30% per il traffico HTTPS nei casi delle applicazioni per conducenti e passeggeri. Inoltre, QUIC ci ha fornito un controllo end-to-end sui pacchetti degli utenti.
In questo articolo condividiamo la nostra esperienza nell'ottimizzazione di TCP per le applicazioni Uber utilizzando uno stack che supporta QUIC.
L'ultima parola della tecnologia: TCP
Oggi TCP è il protocollo di trasporto più utilizzato per la consegna del traffico HTTPS su Internet. TCP garantisce un flusso di byte affidabile, gestendo così i problemi di congestione della rete e le perdite a livello di canale. L'ampio utilizzo di TCP per il traffico HTTPS è spiegato dalla sua onnipresenza (quasi ogni sistema operativo include TCP), dalla sua disponibilità su gran parte dell'infrastruttura (ad esempio, su bilanciatori di carico, proxy HTTPS e CDN) e dalle funzionalità "pronte all'uso" disponibili praticamente su quasi tutte le piattaforme e reti.
La maggior parte degli utenti utilizza la nostra applicazione mentre è in movimento, e i ritardi "tail" di TCP erano lontani dalle esigenze del nostro traffico HTTPS in tempo reale. In poche parole, questo è un problema riscontrato dagli utenti in tutto il mondo – come mostrato nella Figura 1, i ritardi nelle grandi città sono riflessi:
Figura 1. I ritardi "tail" variano nelle principali città dove è presente Uber.
Sebbene i ritardi nelle reti indiane e brasiliane fossero superiori a quelli degli Stati Uniti e del Regno Unito, i ritardi tail erano significativamente più alti rispetto ai ritardi medi. Questo vale anche per gli Stati Uniti e il Regno Unito.
Prestazioni di TCP in modalità wireless
TCP è stato creato per reti cablate, cioè con un focus su collegamenti ben prevedibili. Tuttavia, le reti wireless hanno le loro peculiarità e difficoltà. Innanzitutto, le reti wireless sono sensibili alla perdita di dati a causa di interferenze e attenuazione del segnale. Ad esempio, le reti Wi-Fi sono vulnerabili ai microonde, Bluetooth e altre onde radio. Le reti cellulari soffrono di perdita di segnale () a causa della riflessione/assorbimento del segnale da parte di oggetti e strutture, nonché di da torri Questo porta a ritardi circolari (RTT) e perdite di pacchetti significativamente più grandi (da 4 a 10 volte) e variabili rispetto a una connessione cablata. e perdite di pacchetti rispetto a una connessione cablata.
Per affrontare le fluttuazioni della larghezza di banda e le perdite, le reti cellulari utilizzano solitamente grandi buffer per i picchi di traffico. Ciò può portare a un'eccessiva ritrasmissione, il che significa maggiori ritardi. Spesso, TCP interpreta tale ritrasmissione come una perdita a causa di un time-out aumentato, quindi TCP tende a effettuare ritrasmissioni e a riempire il buffer. Questo problema è noto come (), e rappresenta un internet moderno.
Infine, le prestazioni delle reti mobili variano a seconda dell'operatore, della regione e del momento. Nella Figura 2 abbiamo raccolto i ritardi mediani del traffico HTTPS per celle in un raggio di 2 chilometri. I dati sono stati raccolti per i due maggiori operatori di telefonia mobile a Delhi, India. Come si può notare, le prestazioni variano da cella a cella. Inoltre, le prestazioni di un operatore differiscono da quelle del secondo. Ciò è influenzato da fattori quali i modelli di accesso alla rete in base al tempo e alla posizione, la mobilità degli utenti e l'infrastruttura di rete considerando la densità delle torri e il rapporto dei tipi di rete (LTE, 3G, ecc.).
Figura 2. Ritardi su un raggio di 2 chilometri. Delhi, India.
Inoltre, le prestazioni delle reti mobili variano anche nel tempo. Nella Figura 3 è mostrato il ritardo mediano nei giorni della settimana. Abbiamo anche osservato differenze su scala più ridotta – all'interno di un singolo giorno e ora.
Figura 3. I ritardi possono variare significativamente in giorni diversi ma all'interno dello stesso operatore.
Tutto ciò sopra menzionato porta a un'inefficienza nelle prestazioni di TCP nelle reti wireless. Tuttavia, prima di cercare alternative a TCP, desideravamo sviluppare una comprensione precisa sui seguenti punti:
- è TCP il principale colpevole dei ritardi a coda nelle nostre applicazioni?
- Le reti moderne presentano ritardi circolari significativi e variabili (RTT)?
- Qual è l'impatto di RTT e perdite sulle prestazioni di TCP?
Analisi delle prestazioni di TCP
Per capire come abbiamo analizzato le prestazioni di TCP, ricordiamo brevemente come TCP trasmette dati dal mittente al destinatario. Inizialmente, il mittente stabilisce una connessione TCP eseguendo un handshake : il mittente invia un pacchetto SYN, attende un pacchetto SYN-ACK dal destinatario e poi invia un pacchetto ACK. Ulteriori passaggi due e tre vengono utilizzati per stabilire la connessione TCP. Il destinatario conferma la ricezione di ogni pacchetto (ACK) per garantire la consegna affidabile.
Se un pacchetto o un ACK viene perso, il mittente esegue un retransmit dopo un timeout (RTO, ). RTO viene calcolato dinamicamente, basandosi su vari fattori, come il RTT atteso tra mittente e destinatario.
Figura 4. Lo scambio di pacchetti tramite TCP/TLS include meccanismi di ritrasmissione.
Per determinare come TCP funzionasse nelle nostre applicazioni, abbiamo monitorato i pacchetti TCP utilizzando per una settimana sul traffico di produzione proveniente dai server di confine indiani. Successivamente, abbiamo analizzato le connessioni TCP con . Inoltre, abbiamo sviluppato un'applicazione Android che invia traffico emulato a un server di test, mimando al massimo il traffico reale. Gli smartphone con questa applicazione sono stati distribuiti a diversi dipendenti, che hanno raccolto i log per diversi giorni.
I risultati di entrambi gli esperimenti erano coerenti tra loro. Abbiamo osservato elevate latenze RTT; i valori estremi erano quasi sei volte superiori alla mediana; la media delle latenze superava un secondo. Molte connessioni presentavano perdite, costringendo TCP a ritrasmettere il 3,5% di tutti i pacchetti. In aree sovraccariche, come aeroporti e stazioni, abbiamo registrato perdite del 7%. Questi risultati mettono in discussione il comune pensiero secondo cui gli schemi di trasmissione ridurranno significativamente le perdite a livello di trasporto. Di seguito sono riportati i risultati dei test dall'applicazione 'simulatore':
Metriche di rete
Valori
RTT, millisecondi [50%, 75%, 95%, 99%]
[350, 425, 725, 2300]
Scostamento RTT, secondi
In media ~1,2 s
Perdita di pacchetti in connessioni instabili
In media ~3.5% (7% in aree sovraccariche)
Quasi la metà di queste connessioni ha registrato almeno una perdita di pacchetti, prevalentemente pacchetti SYN e SYN-ACK. La maggior parte delle implementazioni TCP utilizza un valore RTO di 1 secondo per i pacchetti SYN, che aumenta esponenzialmente per le perdite successive. Il tempo di caricamento dell'applicazione può aumentare poiché TCP necessita di più tempo per stabilire le connessioni.
Nel caso dei pacchetti dati, valori elevati di RTO riducono notevolmente l'utilizzo efficace della rete in presenza di perdite temporanee nelle reti wireless. Abbiamo scoperto che il tempo medio di ritrasmissione è di circa 1 secondo, con un ritardo di coda di quasi 30 secondi. Questi elevati ritardi a livello TCP causavano timeout HTTPS e richieste ripetute, aumentando ulteriormente il ritardo e l'inefficienza della rete.
Mentre il 75° percentile dei RTT misurati era intorno ai 425 ms, il 75° percentile per TCP era quasi di 3 secondi. Questo suggerisce che le perdite costringevano TCP a effettuare 7-10 passaggi per trasmettere con successo i dati. Questo può essere il risultato di un calcolo inefficace dell'RTO, dell'incapacità di TCP di rispondere rapidamente alla perdita nella finestra e inefficienza dell'algoritmo di gestione del sovraccarico, che non distingue tra perdite wireless e perdite dovute a sovraccarico di rete. Di seguito sono riportati i risultati dei test sulle perdite TCP:
Statistiche sulle perdite di pacchetti TCP
Significato
Percentuale di connessioni con almeno 1 perdita di pacchetto
45%
Percentuale di connessioni con perdite durante l'instaurazione della connessione
30%
Percentuale di connessioni con perdite durante lo scambio di dati
76%
Distribuzione dei ritardi nelle ritrasmissioni, secondi [50%, 75%, 95%, 99%]
[1, 2.8, 15, 28]
Distribuzione del numero di ritrasmissioni per un pacchetto o segmento TCP
[1,3,6,7]
Applicazione di QUIC
Originariamente progettato da Google, QUIC è un protocollo di trasporto moderno e multistream che funziona sopra UDP. Attualmente QUIC è in (abbiamo già accennato che esistono come due versioni di QUIC, i curiosi – nota del traduttore). Come mostrato nella Figura 5, QUIC si colloca sotto HTTP/3 (in effetti, HTTP/2 sopra QUIC è ciò che è HTTP/3, che attualmente viene intensamente standardizzato). Sostituisce parzialmente i livelli HTTPS e TCP, utilizzando UDP per la formazione dei pacchetti. QUIC supporta solo la trasmissione sicura dei dati, poiché TLS è completamente integrato in QUIC.

Figura 5: QUIC opera sotto HTTP/3, sostituendo TLS, che prima operava sotto HTTP/2.
Di seguito, elenchiamo le ragioni che ci hanno convinti a utilizzare QUIC per rafforzare TCP:
- 0-RTT stabilimento della connessione. QUIC consente il riutilizzo delle autorizzazioni delle connessioni precedenti, riducendo il numero di handshake di sicurezza. In futuro sosterrà 0-RTT, tuttavia l'handshake TCP trifase sarà ancora obbligatorio.
- superamento del blocco HoL. HTTP/2 utilizza una singola connessione TCP per ogni cliente per migliorare le prestazioni, ma ciò può portare a un blocco HoL (head-of-line). QUIC semplifica il multiplexing e consegna le richieste all'applicazione in modo indipendente l'una dall'altra.
- gestione del sovraccarico. QUIC opera a livello applicativo, consentendo di aggiornare più facilmente l'algoritmo di trasporto principale, che gestisce l'invio, basandosi sulle condizioni della rete (numero di perdite o RTT). La maggior parte delle implementazioni TCP utilizza l'algoritmo , che non è ottimale per i traffici sensibili alla latenza. Algoritmi recentemente sviluppati come , modellano la rete in modo più preciso e ottimizzano i ritardi. QUIC consente di utilizzare BBR e aggiornare questo algoritmo man mano che .
- il recupero delle perdite. QUIC attiva due TLP () prima che si attivi il RTO – anche quando le perdite sono molto evidenti. Questo è diverso dalle implementazioni TCP. Il TLP ritrasmette principalmente l'ultimo pacchetto (o uno nuovo, se disponibile), per avviare un recupero rapido. La gestione delle perdite di coda è particolarmente utile per come Uber interagisce con la rete, ovvero per trasmissioni brevi, episodiche e sensibili ai ritardi.
- ACK ottimizzato. Poiché ogni pacchetto ha un numero di sequenza unico, non si presenta il problema pacchetti durante la loro ritrasmissione. I pacchetti ACK contengono anche il tempo per elaborare il pacchetto e generare l'ACK sul lato client. Queste caratteristiche garantiscono che QUIC calcoli in modo più accurato il RTT. L'ACK in QUIC supporta fino a 256 intervalli. , aiutando il mittente a essere più resistente alla riorganizzazione dei pacchetti e a utilizzare meno byte durante il processo. L'ACK selettivo () in TCP non risolve questo problema in tutti i casi.
- migrazione della connessione. Le connessioni QUIC sono identificate da un ID a 64 bit, quindi se il client cambia indirizzo IP, è possibile continuare a utilizzare l'ID della vecchia connessione sul nuovo indirizzo IP, senza interruzioni. Questa è una pratica molto comune per le applicazioni mobili, quando l'utente passa tra Wi-Fi e connessioni cellulari.
Alternative a QUIC
Abbiamo esaminato approcci alternativi per risolvere il problema prima di scegliere QUIC.
Per prima cosa, abbiamo provato a implementare i TPC PoPs (Points of Presence) per terminare le connessioni TCP più vicino agli utenti. Fondamentalmente, i PoPs terminano la connessione TCP con il dispositivo mobile più vicino alla rete cellulare e inoltrano il traffico alla infrastruttura originale. Terminando il TCP più vicino, possiamo potenzialmente ridurre il RTT e essere certi che il TCP risponda in modo più attivo all'ambiente wireless dinamico. Tuttavia, i nostri esperimenti hanno mostrato che in gran parte il RTT e le perdite provengono dalle reti cellulari e l'uso dei PoPs non offre un miglioramento significativo delle performance.
Abbiamo anche esaminato l'ottimizzazione dei parametri TCP. La configurazione dello stack TCP sui nostri server di confine eterogenei è stata difficile, poiché TCP ha implementazioni non comparabili nelle diverse versioni dei sistemi operativi. È stato difficile implementare e testare varie configurazioni di rete. La configurazione di TCP su dispositivi mobili era impossibile a causa della mancanza di autorizzazioni. Inoltre, funzionalità come le connessioni 0-RTT e il miglioramento della previsione RTT sono critiche per l'architettura del protocollo e quindi non è possibile ottenere vantaggi significativi solo ottimizzando TCP.
Infine, abbiamo valutato diversi protocolli basati su UDP che risolvono problemi nel video streaming – volevamo capire se questi protocolli potessero aiutare nel nostro caso. Sfortunatamente, mancavano molto di molte impostazioni di sicurezza e richiedevano anche un'ulteriore connessione TCP per i metadati e le informazioni di controllo.
Le nostre ricerche hanno mostrato che QUIC è praticamente l'unico protocollo che può aiutare con il problema del traffico Internet, tenendo conto sia della sicurezza che delle prestazioni.
Integrazione di QUIC nella piattaforma
Per integrare con successo QUIC e migliorare le prestazioni dell'applicazione in condizioni di connessione scadente, abbiamo sostituito il vecchio stack (HTTP/2 sopra TLS/TCP) con il protocollo QUIC. Abbiamo utilizzato la libreria di rete di , che contiene la versione originale del protocollo Google – gQUIC. Questa implementazione è costantemente migliorata per seguire l'ultima specifica IETF.
Inizialmente, abbiamo integrato Cronet nelle nostre applicazioni Android per aggiungere il supporto per QUIC. L'integrazione è stata effettuata in modo da ridurre al minimo i costi di migrazione. Invece di sostituire completamente lo stack di rete esistente che utilizzava la libreria , abbiamo integrato Cronet SOTTO il framework OkHttp API. Effettuando l'integrazione in questo modo, abbiamo evitato modifiche alle nostre chiamate di rete (utilizzate da ) a livello API.
Analogamente all'approccio per i dispositivi Android, abbiamo implementato Cronet nelle applicazioni Uber per iOS, intercettando il traffico HTTP dalle reti , utilizzando . Questa astrazione, fornita da iOS Foundation, gestisce i dati URL specifici del protocollo e garantisce che possiamo integrare Cronet nelle nostre applicazioni iOS senza costi di migrazione significativi.
Terminazione QUIC sui bilanciatori di carico di Google Cloud
Dalla parte del backend, la terminazione QUIC è garantita dall'infrastruttura di Google Cloud Load balancing, che utilizza le intestazioni nelle risposte per supportare QUIC. In generale, a ogni richiesta HTTP, il bilanciatore aggiunge un'intestazione alt-svc e verifica così il supporto QUIC per il dominio. Quando il client Cronet riceve una risposta HTTP con tale intestazione, utilizza QUIC per le successive richieste HTTP a quel dominio. Una volta che il bilanciatore termina QUIC, la nostra infrastruttura invia esplicitamente questa azione su HTTP2/TCP ai nostri data center.
Prestazioni: risultati
Le prestazioni erogate sono la principale ragione della nostra ricerca del miglior protocollo. Per iniziare, abbiamo creato un banco di prova con , per scoprire come si comporterebbe QUIC in diversi profili di rete. Per verificare il funzionamento di QUIC in reti reali, abbiamo condotto esperimenti, viaggiando per Nuova Delhi, utilizzando un traffico di rete emulato molto simile alle chiamate HTTP in un'applicazione per passeggeri.
Esperimento 1
Inventario per l'esperimento:
- dispositivi di test Android con stack OkHttp e Cronet, per assicurarci che instradiamo il traffico HTTPS tramite TCP e QUIC rispettivamente;
- server di emulazione basato su Java, che invia intestazioni HTTPS omogenee nelle risposte e mette sotto pressione i dispositivi client per ricevere richieste da essi;
- proxy cloud, fisicamente situati vicino all'India, per completare le connessioni TCP e QUIC. Mentre per completare il TCP abbiamo usato un proxy inverso su , è stato difficile trovare un proxy inverso open source per QUIC. Abbiamo assemblato un proxy inverso per QUIC noi stessi, utilizzando lo stack di base QUIC da Chromium e esso in Chromium come open source.
Figura 6. Il set di test per TCP vs QUIC consisteva in dispositivi Android con OkHttp e Cronet, proxy cloud per completare le connessioni e il server di emulazione.
Esperimento 2
Quando Google ha reso QUIC disponibile tramite , abbiamo utilizzato lo stesso inventario, ma con una modifica: invece di NGINX, abbiamo utilizzato i bilanciatori di carico di Google per terminare le connessioni TCP e QUIC dai dispositivi, oltre a reindirizzare il traffico HTTPS al server di emulazione. I bilanciatori sono distribuiti in tutto il mondo, ma utilizzano il server PoP più vicino al dispositivo (grazie alla geolocalizzazione).
Figura 7. Nel secondo esperimento volevamo confrontare la latenza di terminazione TCP e QUIC: utilizzando Google Cloud e il nostro proxy cloud.
In conclusione, abbiamo fatto alcune scoperte:
- la terminazione tramite PoP ha migliorato le prestazioni di TCP. Poiché i bilanciatori terminano la connessione TCP più vicino agli utenti e sono ottimizzati, questo porta a RTT più ridotti, migliorando le prestazioni di TCP. E anche se l'impatto su QUIC è stato minore, ha comunque superato TCP in termini di riduzione delle latenze di coda (dal 10 al 30 percento).
- influenzano le code . Sebbene il nostro proxy QUIC fosse più lontano dai dispositivi (circa 50 ms di latenza in più) rispetto ai bilanciatori di carico di Google, ha mostrato prestazioni simili: una riduzione delle latenze del 15% contro una riduzione del 20% nel 99° percentile di TCP. Ciò indica che il passaggio nell'ultima miglio è un collo di bottiglia nella rete.
Figura 8. I risultati di due esperimenti mostrano che QUIC supera significativamente TCP.
Traffico di produzione
Ispirati dagli esperimenti, abbiamo implementato il supporto per QUIC nelle nostre app Android e iOS. Abbiamo condotto test A/B per valutare l'impatto di QUIC nelle città in cui è presente Uber. In generale, abbiamo osservato una significativa riduzione delle latenze di coda sia a livello regionale che tra operatori telefonici e tipi di rete.
Nei grafici qui sotto sono mostrati i miglioramenti percentuali delle code (95° e 99° percentili) per macroregioni e diversi tipi di rete: LTE, 3G, 2G.
Figura 9. Nei test di produzione, QUIC ha superato TCP in termini di latenza.
Solo avanti
Probabilmente, questo è solo l'inizio: il lancio di QUIC in produzione ha fornito straordinarie opportunità per migliorare le prestazioni delle applicazioni sia in reti stabili che instabili, in particolare:
Aumento della copertura
Analizzando le prestazioni del protocollo su traffico reale, abbiamo notato che circa l'80% delle sessioni ha utilizzato con successo QUIC per tutti le richieste, mentre il 15% delle sessioni ha utilizzato una combinazione di QUIC e TCP. Supponiamo che questa combinazione si sia verificata perché la libreria Cronet torna a utilizzare TCP a causa di timeout, poiché non riesce a distinguere tra veri fallimenti UDP e cattive condizioni di rete. Attualmente stiamo cercando una soluzione a questo problema mentre lavoriamo su un'implementazione successiva di QUIC.
Ottimizzazione di QUIC
Il traffico delle applicazioni mobili è sensibile ai ritardi, ma non alla larghezza di banda. Inoltre, le nostre applicazioni sono principalmente utilizzate in reti cellulari. Basandoci su esperimenti, i ritardi finali sono ancora elevati, anche nonostante l'uso di proxy per completare TCP e QUIC vicino agli utenti. Stiamo attivamente cercando modi per migliorare la gestione del sovraccarico e aumentare l'efficienza degli algoritmi di ripristino della perdita di QUIC.
Con questi e altri miglioramenti, intendiamo migliorare l'esperienza utente indipendentemente dalla rete e dalla regione, rendendo il trasporto dei pacchetti più accessibile in tutto il mondo.
Fonte: habr.com
