Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța

Protocolul QUIC este extrem de interesant de urmărit, motiv pentru care ne place să scriem despre el. Dar dacă publicațiile anterioare despre QUIC erau mai degrabă o natură și hardware istoric (istorie locală, dacă doriți), astăzi suntem bucuroși să publicăm o traducere de alt fel - vom vorbi despre aplicarea reală a protocolului în 2019. Mai mult, nu vorbim de infrastructura mica bazata intr-un asa-zis garaj, ci de Uber, care opereaza aproape in toata lumea. Cum au ajuns inginerii companiei decizia de a utiliza QUIC în producție, cum au efectuat testele și ce au văzut după ce l-au lansat în producție - sub tăietură.

Pozele se pot face clic. Bucură-te de lectură!

Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța

Uber are o scară globală și anume 600 de orașe de prezență, în fiecare dintre ele aplicația se bazează în întregime pe internet wireless de la peste 4500 de operatori de telefonie celulară. Utilizatorii se așteaptă ca aplicația să fie nu doar rapidă, ci și în timp real - pentru a realiza acest lucru, aplicația Uber are nevoie de o latență scăzută și de o conexiune foarte fiabilă. Vai, dar stiva HTTP / 2 nu se descurcă bine în rețelele wireless dinamice și predispuse la pierderi. Ne-am dat seama că, în acest caz, performanța scăzută este direct legată de implementările TCP în nucleele sistemului de operare.

Pentru a rezolva problema, am aplicat QUIC, un protocol modern de multiplexare a canalelor care ne oferă mai mult control asupra performanței protocolului de transport. În prezent, grupul de lucru IETF standardizează QUIC ca HTTP / 3.

După teste ample, am ajuns la concluzia că implementarea QUIC în aplicația noastră ar duce la latențe mai mici în comparație cu TCP. Am observat o reducere a intervalului de 10-30% pentru traficul HTTPS în aplicațiile pentru șofer și pasageri. QUIC ne-a oferit, de asemenea, control de la capăt la capăt asupra pachetelor utilizatorilor.

În acest articol, împărtășim experiența noastră în optimizarea TCP pentru aplicațiile Uber folosind o stivă care acceptă QUIC.

Cea mai recentă tehnologie: TCP

Astăzi, TCP este cel mai folosit protocol de transport pentru livrarea traficului HTTPS pe Internet. TCP oferă un flux fiabil de octeți, făcând astfel față congestionării rețelei și pierderilor de nivel de legătură. Utilizarea pe scară largă a TCP pentru traficul HTTPS se datorează ubicuității primului (aproape fiecare sistem de operare conține TCP), disponibilității pe majoritatea infrastructurii (cum ar fi echilibratoarele de încărcare, proxy-urile HTTPS și CDN-urile) și funcționalității disponibile imediate. pe aproape majoritatea platformelor și rețelelor.

Majoritatea utilizatorilor folosesc aplicația noastră din mers, iar latența TCP nu s-a apropiat deloc de cerințele traficului nostru HTTPS în timp real. Mai simplu spus, utilizatorii din întreaga lume s-au confruntat cu acest lucru - Figura 1 arată întârzierile în orașele mari:

Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța
Figura 1: Latența cozii variază în principalele orașe ale Uber.

Deși latența în rețelele indiene și braziliene a fost mai mare decât în ​​SUA și Marea Britanie, latența finală este semnificativ mai mare decât latența medie. Și acest lucru este valabil chiar și pentru SUA și Marea Britanie.

Performanță TCP over the air

TCP a fost creat pentru cu fir rețele, adică cu accent pe legăturile foarte previzibile. In orice caz, fără fir rețelele au propriile lor caracteristici și dificultăți. În primul rând, rețelele fără fir sunt susceptibile la pierderi din cauza interferențelor și atenuării semnalului. De exemplu, rețelele Wi-Fi sunt sensibile la microunde, bluetooth și alte unde radio. Rețelele celulare suferă de pierderi de semnal (calea pierdută) datorită reflectării/absorbției semnalului de către obiecte și clădiri, precum și din interferență din vecinătate turnuri celulare. Acest lucru duce la mai semnificative (4-10 ori) și mai diverse Durată dus-întors (RTT) și pierderi de pachete în comparație cu o conexiune prin cablu.

Pentru a combate fluctuațiile și pierderile de lățime de bandă, rețelele celulare folosesc de obicei tampon mari pentru rafale de trafic. Acest lucru poate duce la coadă excesivă, ceea ce înseamnă întârzieri mai mari. Foarte des, TCP tratează această coadă ca pe o risipă din cauza unui timeout prelungit, astfel încât TCP tinde să transmită și, prin urmare, să umple tamponul. Această problemă este cunoscută ca bufferbloat (tamponare excesivă a rețelei, buffer bloat), și asta este foarte problema serioasa Internet modern.

În cele din urmă, performanța rețelei celulare variază în funcție de operator, regiune și oră. În Figura 2, am colectat întârzierile mediane ale traficului HTTPS între celule într-un interval de 2 kilometri. Date colectate pentru doi operatori celulari majori din Delhi, India. După cum puteți vedea, performanța variază de la celulă la celulă. De asemenea, productivitatea unui operator diferă de productivitatea celui de-al doilea. Acest lucru este influențat de factori precum tiparele de intrare în rețea, luând în considerare timpul și locația, mobilitatea utilizatorilor, precum și infrastructura rețelei, luând în considerare densitatea turnurilor și raportul dintre tipurile de rețea (LTE, 3G etc.).

Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța
Figura 2. Întârzieri folosind o rază de 2 km ca exemplu. Delhi, India.

De asemenea, performanța rețelelor celulare variază în timp. Figura 3 arată latența medie în funcție de ziua săptămânii. De asemenea, am observat diferențe la o scară mai mică, într-o singură zi și oră.

Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța
Figura 3. Întârzierile cozii pot varia semnificativ între zile, dar pentru același operator.

Toate cele de mai sus fac ca performanța TCP să fie ineficientă în rețelele wireless. Cu toate acestea, înainte de a căuta alternative la TCP, am dorit să dezvoltăm o înțelegere precisă a următoarelor puncte:

  • este TCP principalul vinovat din spatele latențelor de coadă în aplicațiile noastre?
  • Rețelele moderne au întârzieri semnificative și variate dus-întors (RTT)?
  • Care este impactul RTT și pierderea asupra performanței TCP?

Analiza performanței TCP

Pentru a înțelege cum am analizat performanța TCP, să aruncăm o privire rapidă asupra modului în care TCP transferă date de la un expeditor la un receptor. În primul rând, expeditorul stabilește o conexiune TCP, efectuând o conexiune cu trei căi strângere de mână: Expeditorul trimite un pachet SYN, așteaptă un pachet SYN-ACK de la receptor, apoi trimite un pachet ACK. O a doua și a treia trecere suplimentară este cheltuită pentru a stabili conexiunea TCP. Destinatarul confirmă primirea fiecărui pachet (ACK) pentru a asigura o livrare fiabilă.

Dacă se pierde un pachet sau ACK, expeditorul retransmite după un timeout (RTO, timeout de retransmisie). RTO este calculat dinamic pe baza diverșilor factori, cum ar fi întârzierea RTT așteptată între expeditor și destinatar.

Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța
Figura 4. Schimbul de pachete prin TCP/TLS include un mecanism de retransmisie.

Pentru a determina cum a funcționat TCP în aplicațiile noastre, am monitorizat pachetele TCP folosind tcpdump timp de o săptămână în trafic de luptă care vine de la serverele edge indiene. Am analizat apoi conexiunile TCP folosind tcptrace. În plus, am creat o aplicație Android care trimite trafic emulat către un server de testare, imitând pe cât posibil traficul real. Smartphone-urile cu această aplicație au fost distribuite mai multor angajați, care au colectat jurnalele pe parcursul mai multor zile.

Rezultatele ambelor experimente au fost în concordanță între ele. Am văzut latențe RTT ridicate; valorile cozii au fost de aproape 6 ori mai mari decât valoarea mediană; media aritmetică a întârzierilor este mai mare de 1 secundă. Multe conexiuni au avut pierderi, ceea ce a făcut ca TCP să retransmită 3,5% din toate pachetele. În zonele aglomerate, cum ar fi aeroporturile și gările, am înregistrat pierderi de 7%. Aceste rezultate pun la îndoială înțelepciunea convențională a celor folosite în rețelele celulare circuite avansate de retransmisie reduce semnificativ pierderile la nivel de transport. Mai jos sunt rezultatele testelor din aplicația „simulator”:

Valori de rețea
sens

RTT, milisecunde [50%,75%, 95%,99%]
[350, 425, 725, 2300]

Divergență RTT, secunde
În medie ~1,2 s

Pierdere de pachete pe conexiuni instabile
Medie ~3.5% (7% în zone supraîncărcate)

Aproape jumătate dintre aceste conexiuni au avut cel puțin o pierdere de pachet, majoritatea pachete SYN și SYN-ACK. Majoritatea implementărilor TCP utilizează o valoare RTO de 1 secundă pentru pachetele SYN, care crește exponențial pentru pierderile ulterioare. Timpii de încărcare a aplicațiilor pot crește, deoarece TCP durează mai mult pentru a stabili conexiunile.

În cazul pachetelor de date, valorile RTO ridicate reduc foarte mult utilizarea utilă a rețelei în prezența pierderilor tranzitorii în rețelele wireless. Am descoperit că timpul mediu de retransmisie este de aproximativ 1 secundă, cu o întârziere de aproape 30 de secunde. Aceste latențe mari la nivel TCP au cauzat expirări HTTPS și solicitări din nou, crescând și mai mult latența și ineficiența rețelei.

În timp ce percentila 75 a RTT măsurată a fost de aproximativ 425 ms, percentila 75 pentru TCP a fost de aproape 3 secunde. Acest lucru sugerează că pierderea a făcut ca TCP să facă 7-10 treceri pentru a transmite cu succes date. Aceasta poate fi o consecință a calculului ineficient al RTO, a incapacității TCP de a răspunde rapid la pierderi. ultimele pachete în fereastră și ineficiența algoritmului de control al congestiei, care nu face distincția între pierderile wireless și pierderile datorate congestiei rețelei. Mai jos sunt rezultatele testelor de pierdere TCP:

Statistici privind pierderile de pachete TCP
Valoare

Procentul de conexiuni cu cel puțin 1 pierdere de pachet
45%

Procentul de conexiuni cu pierderi în timpul configurării conexiunii
30%

Procentul de conexiuni cu pierderi în timpul schimbului de date
76%

Distribuția întârzierilor în retransmisie, secunde [50%, 75%, 95%,99%] [1, 2.8, 15, 28]

Distribuția numărului de retransmisii pentru un pachet sau segment TCP
[1,3,6,7]

Aplicarea QUIC

Dezvoltat inițial de Google, QUIC este un protocol de transport modern cu mai multe fire care rulează pe UDP. În prezent, QUIC este în proces de standardizare (am scris deja că există, parcă, două versiuni de QUIC, curioase pot urma linkul – aprox. traducător). După cum se arată în Figura 5, QUIC este plasat sub HTTP/3 (de fapt, HTTP/2 peste QUIC este HTTP/3, care acum este standardizat intens). Acesta înlocuiește parțial straturile HTTPS și TCP prin utilizarea UDP pentru a forma pachete. QUIC acceptă doar transferul securizat de date, deoarece TLS este complet integrat în QUIC.

Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța
Figura 5: QUIC rulează sub HTTP/3, înlocuind TLS, care rula anterior sub HTTP/2.

Mai jos sunt motivele care ne-au convins să folosim QUIC pentru amplificarea TCP:

  • Stabilirea conexiunii 0-RTT. QUIC permite reutilizarea autorizațiilor de la conexiunile anterioare, reducând numărul de strângeri de mână de securitate. In viitor TLS1.3 va suporta 0-RTT, dar va fi în continuare necesară o strângere de mână TCP în trei căi.
  • depășirea blocării HoL. HTTP/2 utilizează o conexiune TCP per client pentru a îmbunătăți performanța, dar acest lucru poate duce la blocarea HoL (head-of-line). QUIC simplifică multiplexarea și livrează cererile aplicației în mod independent.
  • controlul congestiei. QUIC se află la nivelul aplicației, facilitând actualizarea algoritmului principal de transport care controlează trimiterea pe baza parametrilor de rețea (număr de pierderi sau RTT). Majoritatea implementărilor TCP folosesc algoritmul CUB, care nu este optim pentru traficul sensibil la latență. Algoritmi dezvoltați recent, cum ar fi BBR, modelați mai precis rețeaua și optimizați latența. QUIC vă permite să utilizați BBR și să actualizați acest algoritm pe măsură ce este utilizat. îmbunătățirea.
  • refacerea pierderilor. QUIC apelează două TLP-uri (sonda de pierdere a cozii) înainte ca RTO să fie declanșat - chiar și atunci când pierderile sunt foarte vizibile. Acest lucru este diferit de implementările TCP. TLP retransmite în principal ultimul pachet (sau cel nou, dacă există) pentru a declanșa o completare rapidă. Gestionarea întârzierilor este deosebit de utilă pentru modul în care Uber își operează rețeaua, și anume pentru transferurile de date scurte, sporadice și sensibile la latență.
  • ACK optimizat. Deoarece fiecare pachet are un număr unic de secvență, nu există nicio problemă distincții pachetele atunci când sunt retransmise. Pachetele ACK conțin, de asemenea, timp pentru a procesa pachetul și a genera un ACK pe partea clientului. Aceste caracteristici asigură că QUIC calculează RTT mai precis. ACK în QUIC acceptă până la 256 de benzi NACK, ajutând expeditorul să fie mai rezistent la amestecarea pachetelor și să utilizeze mai puțini octeți în proces. ACK selectiv (SAC) în TCP nu rezolvă această problemă în toate cazurile.
  • migrarea conexiunii. Conexiunile QUIC sunt identificate printr-un ID pe 64 de biți, așa că dacă un client modifică adresele IP, vechiul ID de conexiune poate fi folosit în continuare pe noua adresă IP fără întrerupere. Aceasta este o practică foarte comună pentru aplicațiile mobile în care utilizatorul comută între conexiunile Wi-Fi și celulare.

Alternative la QUIC

Am luat în considerare abordări alternative pentru rezolvarea problemei înainte de a alege QUIC.

Primul lucru pe care l-am încercat a fost să implementăm TPC PoP-uri (Points of Presence) pentru a termina conexiunile TCP mai aproape de utilizatori. În esență, PoP-urile termină o conexiune TCP cu un dispozitiv mobil mai aproape de rețeaua celulară și redirecționează traficul către infrastructura originală. Prin terminarea TCP mai aproape, putem reduce RTT-ul și ne putem asigura că TCP este mai receptiv la un mediu wireless dinamic. Cu toate acestea, experimentele noastre au arătat că cea mai mare parte a RTT și a pierderilor provin din rețelele celulare, iar utilizarea PoP-urilor nu oferă o îmbunătățire semnificativă a performanței.

Ne-am uitat și la reglarea parametrilor TCP. Configurarea unei stive TCP pe serverele noastre marginale eterogene a fost dificilă, deoarece TCP are implementări disparate în diferite versiuni ale sistemului de operare. A fost dificil să implementezi acest lucru și să testezi diferite configurații de rețea. Configurarea TCP direct pe dispozitivele mobile nu a fost posibilă din cauza lipsei de permisiuni. Mai important, caracteristici precum conexiunile 0-RTT și predicția RTT îmbunătățită sunt esențiale pentru arhitectura protocolului și, prin urmare, este imposibil să se obțină beneficii semnificative doar prin reglarea TCP.

În cele din urmă, am evaluat mai multe protocoale bazate pe UDP care depanează fluxul video - am vrut să vedem dacă aceste protocoale ar ajuta în cazul nostru. Din păcate, le lipseau foarte multe setări de securitate și necesitau, de asemenea, o conexiune TCP suplimentară pentru metadate și informații de control.

Cercetările noastre au arătat că QUIC este poate singurul protocol care poate ajuta la problema traficului pe Internet, ținând cont atât de securitate, cât și de performanță.

Integrarea QUIC în platformă

Pentru a încorpora cu succes QUIC și a îmbunătăți performanța aplicațiilor în medii de conectivitate slabă, am înlocuit vechea stivă (HTTP/2 peste TLS/TCP) cu protocolul QUIC. Am folosit biblioteca de rețea Cronet de Proiecte Chromium, care conține versiunea originală Google a protocolului - gQUIC. Această implementare este, de asemenea, îmbunătățită în mod constant pentru a respecta cele mai recente specificații IETF.

Am integrat mai întâi Cronet în aplicațiile noastre Android pentru a adăuga suport pentru QUIC. Integrarea a fost realizată în așa fel încât să reducă cât mai mult posibil costurile de migrare. În loc să înlocuiți complet vechea stivă de rețea care folosea biblioteca OkHttp, am integrat Cronet ÎN cadrul API-ului OkHttp. Făcând integrarea în acest fel, am evitat modificările la apelurile noastre de rețea (care sunt folosite de Retrofit) la nivel de API.

Similar cu abordarea pentru dispozitivele Android, am implementat Cronet în aplicațiile Uber de pe iOS, interceptând traficul HTTP din rețea APIfolosind NSURLProtocol. Această abstractizare, furnizată de Fundația iOS, gestionează datele URL specifice protocolului și ne asigură că putem integra Cronet în aplicațiile noastre iOS fără costuri semnificative de migrare.

Finalizarea QUIC pe Google Cloud Balancers

Pe partea de backend, finalizarea QUIC este asigurată de infrastructura Google Cloud Load balancing, care utilizează alt-svc anteturi ca răspunsuri pentru a sprijini QUIC. În general, echilibratorul adaugă un antet alt-svc la fiecare solicitare HTTP, iar acest lucru validează deja suportul QUIC pentru domeniu. Când un client Cronet primește un răspuns HTTP cu acest antet, folosește QUIC pentru solicitările HTTP ulterioare către acel domeniu. Odată ce echilibratorul finalizează QUIC, infrastructura noastră trimite în mod explicit această acțiune prin HTTP2/TCP către centrele noastre de date.

Performanță: Rezultate

Performanța de ieșire este principalul motiv pentru căutarea unui protocol mai bun. Pentru început, am creat un stand cu emularea rețeleipentru a afla cum se va comporta QUIC sub diferite profiluri de rețea. Pentru a testa performanța QUIC în rețelele din lumea reală, am derulat experimente în timp ce conducem prin New Delhi utilizând trafic de rețea emulat foarte similar cu apelurile HTTP din aplicația pentru pasageri.

Experimentul 1

Echipament pentru experiment:

  • testați dispozitivele Android cu stive OkHttp și Cronet pentru a vă asigura că permitem traficul HTTPS prin TCP și, respectiv, QUIC;
  • un server de emulare bazat pe Java care trimite același tip de anteturi HTTPS în răspunsuri și încarcă dispozitivele client pentru a primi cereri de la acestea;
  • proxy-uri cloud care sunt situate fizic aproape de India pentru a termina conexiunile TCP și QUIC. În timp ce pentru terminarea TCP am folosit un proxy invers Nginx, a fost dificil să găsești un proxy invers cu sursă deschisă pentru QUIC. Am construit un proxy invers pentru QUIC folosind stiva de bază QUIC de la Chromium și publicat în crom ca sursă deschisă.

Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanțaProtocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța
Figura 6. Suita de teste rutiere TCP vs QUIC a constat din dispozitive Android cu OkHttp și Cronet, proxy-uri cloud pentru terminarea conexiunilor și un server de emulare.

Experimentul 2

Când Google a pus la dispoziție QUIC cu Google Cloud Load Balancing, am folosit același inventar, dar cu o singură modificare: în loc de NGINX, am luat dispozitive de echilibrare a încărcăturii Google pentru a termina conexiunile TCP și QUIC de pe dispozitive, precum și pentru a direcționa traficul HTTPS către serverul de emulare. Echilibratoarele sunt distribuite în toată lumea, dar folosesc serverul PoP cel mai apropiat de dispozitiv (datorită geolocalizării).

Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța
Figura 7. În al doilea experiment, am vrut să comparăm latența de finalizare a TCP și QUIC: folosind Google Cloud și folosind proxy-ul nostru cloud.

Drept urmare, ne-au așteptat câteva dezvăluiri:

  • terminarea prin PoP a îmbunătățit performanța TCP. Deoarece echilibratorii termină conexiunile TCP mai aproape de utilizatori și sunt extrem de optimizați, acest lucru are ca rezultat RTT-uri mai mici, ceea ce îmbunătățește performanța TCP. Și, deși QUIC a fost mai puțin afectat, a depășit totuși TCP în ceea ce privește reducerea latenței cozii (cu 10-30 la sută).
  • cozile sunt afectate hopuri de rețea. Deși proxy-ul nostru QUIC era mai departe de dispozitive (latență cu aproximativ 50 ms mai mare) decât echilibratoarele de încărcare Google, acesta a oferit performanțe similare - o reducere cu 15% a latenței față de o reducere cu 20% în percentila 99 pentru TCP. Acest lucru sugerează că ultima tranziție de milă este un blocaj în rețea.

Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanțaProtocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța
Figura 8: Rezultatele din două experimente arată că QUIC depășește semnificativ TCP.

Combate traficul

Inspirați de experimente, am implementat suportul QUIC în aplicațiile noastre Android și iOS. Am efectuat teste A/B pentru a determina impactul QUIC în orașele în care operează Uber. În general, am observat o reducere semnificativă a întârzierilor de coadă în ambele regiuni, operatorii de telecomunicații și tipul de rețea.

Graficele de mai jos arată îmbunătățirile procentuale ale coziilor (95 și 99 percentile) în funcție de macroregiune și diferite tipuri de rețele - LTE, 3G, 2G.
Protocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanțaProtocolul QUIC în acțiune: cum l-a implementat Uber pentru a optimiza performanța
Figura 9. În testele de luptă, QUIC a depășit TCP în ceea ce privește latența.

Numai înainte

Poate că acesta este doar începutul - lansarea QUIC în producție a oferit oportunități uimitoare de îmbunătățire a performanței aplicațiilor atât în ​​rețelele stabile, cât și în cele instabile, și anume:

Acoperire crescută

După ce am analizat performanța protocolului asupra traficului real, am văzut că aproximativ 80% dintre sesiuni au folosit cu succes QUIC pentru toate solicitări, în timp ce 15% dintre sesiuni au folosit o combinație de QUIC și TCP. Presupunem că combinația se datorează limitării de timp a bibliotecii Cronet înapoi la TCP, deoarece nu poate face distincția între eșecurile UDP reale și condițiile proaste ale rețelei. În prezent, căutăm o soluție la această problemă în timp ce lucrăm pentru implementarea ulterioară a QUIC.

Optimizare QUIC

Traficul din aplicațiile mobile este sensibil la latență, dar nu este sensibil la lățimea de bandă. De asemenea, aplicațiile noastre sunt utilizate în principal pe rețelele celulare. Pe baza experimentelor, latența finală este încă mare, chiar dacă se folosește un proxy pentru a termina TCP și QUIC aproape de utilizatori. Căutăm în mod activ modalități de a îmbunătăți gestionarea congestiei și de a îmbunătăți eficiența algoritmilor de recuperare a pierderilor QUIC.

Cu acestea și cu alte câteva îmbunătățiri, intenționăm să îmbunătățim experiența utilizatorului, indiferent de rețea și regiune, făcând transportul de pachete convenabil și fără întreruperi mai accesibil în întreaga lume.

Sursa: www.habr.com

Adauga un comentariu