Uneori mai mult este mai puțin. Când reducerea sarcinii are ca rezultat creșterea latenței

Ca în majoritatea postărilor, există o problemă cu un serviciu distribuit, să numim acest serviciu Alvin. De data aceasta nu am descoperit problema chiar eu, m-au informat băieții din partea clientului.

Într-o zi m-am trezit cu un e-mail nemulțumit din cauza întârzierilor mari cu Alvin, pe care plănuiam să-l lansăm în viitorul apropiat. Mai exact, clientul a experimentat o latență de percentila 99 în regiunea de 50 ms, cu mult peste bugetul nostru de latență. Acest lucru a fost surprinzător, deoarece am testat serviciul pe larg, în special în ceea ce privește latența, care este o plângere comună.

Înainte de a-l testa pe Alvin, am efectuat o mulțime de experimente cu 40 de interogări pe secundă (QPS), toate arătând o latență mai mică de 10 ms. Eram gata să declar că nu sunt de acord cu rezultatele lor. Dar aruncând o altă privire la scrisoare, am observat ceva nou: nu testasem exact condițiile menționate de ei, QPS-ul lor era mult mai scăzut decât al meu. Am testat la 40k QPS, dar ei doar la 1k. Am mai făcut un experiment, de data aceasta cu un QPS mai mic, doar pentru a-i potoli.

Din moment ce scriu pe blog despre asta, probabil că v-ați dat deja seama că numerele lor erau corecte. Mi-am testat clientul virtual din nou și din nou, cu același rezultat: un număr mic de cereri nu numai că mărește latența, dar crește numărul de solicitări cu o latență de peste 10 ms. Cu alte cuvinte, dacă la 40k QPS aproximativ 50 de solicitări pe secundă depășeau 50 ms, atunci la 1k QPS au fost 100 de solicitări peste 50 ms în fiecare secundă. Paradox!

Uneori mai mult este mai puțin. Când reducerea sarcinii are ca rezultat creșterea latenței

Îngustând căutarea

Când se confruntă cu o problemă de latență într-un sistem distribuit cu multe componente, primul pas este crearea unei liste scurte de suspecți. Să pătrundem puțin mai adânc în arhitectura lui Alvin:

Uneori mai mult este mai puțin. Când reducerea sarcinii are ca rezultat creșterea latenței

Un bun punct de plecare este o listă de tranziții I/O finalizate (apeluri de rețea/căutări pe disc etc.). Să încercăm să ne dăm seama unde este întârzierea. Pe lângă I/O-ul evident cu clientul, Alvin face un pas suplimentar: accesează depozitul de date. Cu toate acestea, această stocare funcționează în același cluster ca și Alvin, astfel încât latența ar trebui să fie mai mică decât la client. Deci, lista suspecților:

  1. Apel în rețea de la client către Alvin.
  2. Apel de rețea de la Alvin către depozitul de date.
  3. Căutați pe disc în depozitul de date.
  4. Apel de rețea de la depozitul de date către Alvin.
  5. Apel de rețea de la Alvin către un client.

Să încercăm să tăiem câteva puncte.

Stocarea datelor nu are nimic de-a face cu asta

Primul lucru pe care l-am făcut a fost să convertesc Alvin într-un server ping-ping care nu procesează cererile. Când primește o solicitare, returnează un răspuns gol. Dacă latența scade, atunci o eroare în implementarea Alvin sau a depozitului de date nu este nimic nemaiauzit. În primul experiment obținem următorul grafic:

Uneori mai mult este mai puțin. Când reducerea sarcinii are ca rezultat creșterea latenței

După cum puteți vedea, nu există nicio îmbunătățire atunci când utilizați serverul ping-ping. Aceasta înseamnă că depozitul de date nu crește latența, iar lista suspecților se reduce la jumătate:

  1. Apel în rețea de la client către Alvin.
  2. Apel de rețea de la Alvin către un client.

Grozav! Lista se micșorează rapid. Credeam că aproape îmi dădusem motivul.

gRPC

Acum este momentul să vă prezint un jucător nou: gRPC. Aceasta este o bibliotecă open source de la Google pentru comunicare în timpul procesului RPC. deși gRPC bine optimizat și utilizat pe scară largă, aceasta a fost prima dată când îl folosesc pe un sistem de această dimensiune și mă așteptam ca implementarea mea să fie suboptimă - cel puțin.

disponibilitate gRPC în stivă a dat naștere la o nouă întrebare: poate este implementarea mea sau a mea gRPC provoacă o problemă de latență? Adăugarea unui nou suspect pe listă:

  1. Clientul sună la bibliotecă gRPC
  2. bibliotecă gRPC efectuează un apel de rețea către biblioteca de pe client gRPC pe server
  3. bibliotecă gRPC contactează Alvin (nicio operațiune în cazul serverului de ping-pong)

Pentru a vă face o idee despre cum arată codul, implementarea mea client/Alvin nu este mult diferită de cea client-server exemple asincrone.

Notă: Lista de mai sus este puțin simplificată deoarece gRPC face posibilă utilizarea propriului model (șablon?) de threading, în care stiva de execuție este împletită gRPC și implementarea utilizatorului. De dragul simplității, vom rămâne la acest model.

Profilarea va rezolva totul

După ce am tăiat depozitele de date, am crezut că aproape am terminat: „Acum e ușor! Să aplicăm profilul și să aflăm unde apare întârzierea.” eu mare fan al profilării de precizie, deoarece procesoarele sunt foarte rapide și de cele mai multe ori nu reprezintă blocajul. Cele mai multe întârzieri apar atunci când procesorul trebuie să oprească procesarea pentru a face altceva. Accurate CPU Profiling face exact asta: înregistrează totul cu precizie schimbă de context și clarifică unde apar întârzierile.

Am luat patru profiluri: cu QPS ridicat (latență scăzută) și cu un server de ping-pong cu QPS scăzut (latență ridicată), atât pe partea de client, cât și pe partea de server. Și, pentru orice eventualitate, am luat și un eșantion de profil de procesor. Când compar profilurile, de obicei caut o stivă de apeluri anormală. De exemplu, în partea proastă, cu latență mare, există mult mai multe comutatoare de context (de 10 ori sau mai mult). Dar în cazul meu, numărul de comutări de context a fost aproape același. Spre groaza mea, nu era nimic semnificativ acolo.

Depanare suplimentară

eram disperat. Nu știam ce alte instrumente aș putea folosi, iar următorul meu plan a fost, în esență, să repet experimentele cu diferite variații, mai degrabă decât să diagnosticez clar problema.

Ce-ar fi dacă

De la bun început, am fost îngrijorat de latența specifică de 50 ms. Acesta este un moment foarte mare. Am decis că voi tăia bucăți din cod până îmi voi putea da seama exact care parte provoacă această eroare. Apoi a venit un experiment care a funcționat.

Ca de obicei, retrospectiv se pare că totul era evident. Am plasat clientul pe aceeași mașină cu Alvin - și am trimis o solicitare către localhost. Și creșterea latenței a dispărut!

Uneori mai mult este mai puțin. Când reducerea sarcinii are ca rezultat creșterea latenței

A fost ceva în neregulă cu rețeaua.

Învățarea abilităților de inginer de rețea

Trebuie să recunosc: cunoștințele mele despre tehnologiile de rețea sunt groaznice, mai ales având în vedere faptul că lucrez cu ele în fiecare zi. Dar rețeaua era principalul suspect și trebuia să învăț cum să o depanez.

Din fericire, internetul îi iubește pe cei care vor să învețe. Combinația dintre ping și tracert părea un început suficient de bun pentru depanarea problemelor de transport în rețea.

În primul rând, am lansat PsPing la portul TCP al lui Alvin. Am folosit setările implicite - nimic special. Din peste o mie de ping-uri, niciunul nu a depășit 10 ms, cu excepția primului pentru încălzire. Acest lucru este contrar creșterii observate a latenței de 50 ms la percentila 99: acolo, pentru fiecare 100 de cereri, ar fi trebuit să vedem aproximativ o cerere cu o latență de 50 ms.

Apoi am încercat tracert: Poate fi o problemă la unul dintre nodurile de-a lungul rutei dintre Alvin și client. Dar urmăritorul s-a întors și cu mâinile goale.

Deci nu codul meu, implementarea gRPC sau rețeaua a cauzat întârzierea. Începeam să-mi fac griji că nu voi înțelege niciodată asta.

Acum pe ce sistem de operare suntem

gRPC folosit pe scară largă pe Linux, dar exotic pe Windows. Am decis să încerc un experiment, care a funcționat: am creat o mașină virtuală Linux, am compilat Alvin pentru Linux și am implementat-o.

Uneori mai mult este mai puțin. Când reducerea sarcinii are ca rezultat creșterea latenței

Și iată ce s-a întâmplat: serverul de ping-pong Linux nu a avut aceleași întârzieri ca o gazdă similară Windows, deși sursa de date nu a fost diferită. Se pare că problema este în implementarea gRPC pentru Windows.

algoritmul lui Nagle

În tot acest timp am crezut că îmi lipsește un steag gRPC. Acum înțeleg ce este cu adevărat gRPC Drapelul Windows lipsește. Am găsit o bibliotecă RPC internă în care eram sigur că va funcționa bine pentru toate indicatoarele setate Winsock. Apoi am adăugat toate aceste steaguri la gRPC și am implementat Alvin pe Windows, într-un server de ping-pong Windows corelat!

Uneori mai mult este mai puțin. Când reducerea sarcinii are ca rezultat creșterea latenței

aproape Gata: am început să elimin steagurile adăugate pe rând până când regresia a revenit, astfel încât să pot identifica cauza. A fost infam TCP_NODELAY, comutatorul de algoritm al lui Nagle.

algoritmul lui Nagle încearcă să reducă numărul de pachete trimise printr-o rețea prin întârzierea transmiterii mesajelor până când dimensiunea pachetului depășește un anumit număr de octeți. Deși acest lucru poate fi plăcut pentru utilizatorul obișnuit, este distructiv pentru serverele în timp real, deoarece sistemul de operare va întârzia unele mesaje, provocând întârzieri la QPS scăzut. U gRPC acest flag a fost setat în implementarea Linux pentru socket-urile TCP, dar nu și în Windows. Eu sunt asta corectat.

Concluzie

Latența mai mare la QPS scăzut a fost cauzată de optimizarea sistemului de operare. Privind retrospectiv, profilarea nu a detectat latența, deoarece a fost făcută mai degrabă în modul kernel decât în modul utilizator. Nu știu dacă algoritmul lui Nagle poate fi observat prin capturi ETW, dar ar fi interesant.

În ceea ce privește experimentul localhost, probabil că nu a atins codul real de rețea și algoritmul lui Nagle nu a rulat, așa că problemele de latență au dispărut când clientul a ajuns la Alvin prin localhost.

Data viitoare când vedeți o creștere a latenței pe măsură ce numărul de solicitări pe secundă scade, algoritmul lui Nagle ar trebui să fie pe lista dumneavoastră de suspecți!

Sursa: www.habr.com

Adauga un comentariu