Niekedy je viac menej. Zníženie záťaže má za následok zvýšenie latencie

Rovnako ako v väčšina príspevkov, vyskytol sa problém s distribuovanou službou, nazvime ju Alvin. Tentokrát som problém nezistil ja, informovali ma chalani zo strany klienta.

Jedného dňa som sa zobudil na nespokojný email kvôli dlhým meškaniam s Alvinom, ktorý sme plánovali spustiť v blízkej budúcnosti. Konkrétne klient zaznamenal 99. percentilovú latenciu v oblasti 50 ms, čo je výrazne nad naším rozpočtom na latenciu. Bolo to prekvapujúce, pretože som službu rozsiahlo testoval, najmä pokiaľ ide o latenciu, čo je bežná sťažnosť.

Predtým, ako som dal Alvina do testovania, vykonal som veľa experimentov so 40 10 dopytmi za sekundu (QPS), pričom všetky vykazovali latenciu menšiu ako 40 ms. Bol som pripravený vyhlásiť, že nesúhlasím s ich výsledkami. Ale pri ďalšom pohľade na list som si všimol niečo nové: podmienky, ktoré spomínali, som presne netestoval, ich QPS bola oveľa nižšia ako moja. Testoval som na 1k QPS, ale oni len na XNUMXk. Spustil som ďalší experiment, tentoraz s nižším QPS, len aby som ich upokojil.

Keďže o tom píšem blog, pravdepodobne ste už prišli na to, že ich čísla boli správne. Svojho virtuálneho klienta som testoval znova a znova, s rovnakým výsledkom: nízky počet požiadaviek nielenže zvyšuje latenciu, ale zvyšuje počet požiadaviek s latenciou viac ako 10 ms. Inými slovami, ak pri 40k QPS asi 50 požiadaviek za sekundu presiahlo 50 ms, potom pri 1k QPS bolo každú sekundu 100 požiadaviek nad 50 ms. Paradox!

Niekedy je viac menej. Zníženie záťaže má za následok zvýšenie latencie

Zúženie vyhľadávania

Keď sa stretnete s problémom latencie v distribuovanom systéme s mnohými komponentmi, prvým krokom je vytvorenie krátkeho zoznamu podozrivých. Poďme trochu hlbšie do Alvinovej architektúry:

Niekedy je viac menej. Zníženie záťaže má za následok zvýšenie latencie

Dobrým východiskovým bodom je zoznam dokončených I/O prechodov (sieťové hovory/vyhľadávanie disku atď.). Pokúsme sa zistiť, kde je oneskorenie. Okrem zrejmého I/O s klientom, Alvin robí ďalší krok: pristupuje k úložisku údajov. Toto úložisko však funguje v rovnakom klastri ako Alvin, takže latencia by tam mala byť menšia ako u klienta. Takže zoznam podozrivých:

  1. Sieťový hovor od klienta k Alvinovi.
  2. Sieťový hovor od Alvina do dátového úložiska.
  3. Vyhľadajte na disku v úložisku údajov.
  4. Sieťový hovor z dátového skladu do Alvina.
  5. Sieťový hovor od Alvina klientovi.

Skúsme odškrtnúť niektoré body.

Ukladanie dát s tým nemá nič spoločné

Prvá vec, ktorú som urobil, bola konverzia Alvina na ping-ping server, ktorý nespracováva požiadavky. Keď dostane požiadavku, vráti prázdnu odpoveď. Ak sa latencia zníži, potom chyba v implementácii Alvin alebo dátového skladu nie je nič neslýchané. V prvom experimente dostaneme nasledujúci graf:

Niekedy je viac menej. Zníženie záťaže má za následok zvýšenie latencie

Ako vidíte, pri používaní servera ping-ping nedochádza k žiadnemu zlepšeniu. To znamená, že dátový sklad nezvyšuje latenciu a zoznam podozrivých sa skráti na polovicu:

  1. Sieťový hovor od klienta k Alvinovi.
  2. Sieťový hovor od Alvina klientovi.

Skvelé! Zoznam sa rýchlo zmenšuje. Myslel som si, že som skoro prišiel na dôvod.

gRPC

Teraz je čas predstaviť vám nového hráča: gRPC. Toto je knižnica s otvoreným zdrojovým kódom od spoločnosti Google na komunikáciu počas procesu RPC, hoci gRPC dobre optimalizované a široko používané, toto bolo prvýkrát, čo som ho použil na systéme takejto veľkosti a očakával som, že moja implementácia nebude prinajmenšom optimálna.

dostupnosť gRPC v zásobníku vyvolala novú otázku: možno je to moja implementácia alebo ja gRPC spôsobuje problém s latenciou? Pridanie nového podozrivého do zoznamu:

  1. Klient zavolá do knižnice gRPC
  2. knižnica gRPC uskutoční sieťové volanie do knižnice na klientovi gRPC na serveri
  3. knižnica gRPC kontaktuje Alvina (žiadna operácia v prípade ping-pong servera)

Aby ste mali predstavu o tom, ako kód vyzerá, moja implementácia klient/Alvin sa príliš nelíši od implementácie klient-server asynchrónne príklady.

Poznámka: Vyššie uvedený zoznam je trochu zjednodušený, pretože gRPC umožňuje použiť vlastný (šablónový?) vláknový model, v ktorom je prepletený exekučný zásobník gRPC a užívateľská implementácia. Pre jednoduchosť zostaneme pri tomto modeli.

Profilovanie všetko napraví

Po preškrtnutí dátových skladov som si myslel, že som takmer hotový: „Teraz je to jednoduché! Aplikujme profil a zistime, kde dochádza k oneskoreniu." ja veľký fanúšik presného profilovania, pretože CPU sú veľmi rýchle a väčšinou nie sú prekážkou. Väčšina oneskorení nastane, keď procesor musí zastaviť spracovanie, aby urobil niečo iné. Presné profilovanie CPU to robí: presne zaznamenáva všetko kontextové prepínače a objasňuje, kde dochádza k oneskoreniu.

Zobral som štyri profily: s vysokým QPS (nízka latencia) a s ping-pongovým serverom s nízkym QPS (vysoká latencia), a to na strane klienta aj na strane servera. A pre každý prípad som zobral aj vzorový profil procesora. Pri porovnávaní profilov zvyčajne hľadám anomálny zásobník hovorov. Napríklad na zlej strane s vysokou latenciou existuje oveľa viac prepínačov kontextu (10-krát alebo viac). Ale v mojom prípade bol počet kontextových prepínačov takmer rovnaký. Na moje zdesenie tam nebolo nič podstatné.

Dodatočné ladenie

Bol som zúfalý. Nevedel som, aké ďalšie nástroje by som mohol použiť, a môj ďalší plán bol v podstate zopakovať experimenty s rôznymi variáciami namiesto toho, aby som jasne diagnostikoval problém.

Čo ak

Od samého začiatku som sa obával konkrétnej latencie 50 ms. Toto je veľmi veľký čas. Rozhodol som sa, že z kódu vystrihnem kúsky, kým presne nezistím, ktorá časť túto chybu spôsobuje. Potom prišiel experiment, ktorý fungoval.

Ako obvykle, pri spätnom pohľade sa zdá, že všetko bolo zrejmé. Klienta som umiestnil na rovnaký stroj ako Alvin – a poslal som mu požiadavku localhost. A zvýšenie latencie je preč!

Niekedy je viac menej. Zníženie záťaže má za následok zvýšenie latencie

Vyskytol sa problém so sieťou.

Naučte sa zručnosti sieťového inžiniera

Musím sa priznať: moje znalosti o sieťových technológiách sú hrozné, najmä vzhľadom na to, že s nimi pracujem každý deň. Ale sieť bola hlavným podozrivým a potreboval som sa naučiť, ako ju odladiť.

Našťastie internet miluje tých, ktorí sa chcú učiť. Kombinácia ping a tracert sa zdala ako dostatočne dobrý začiatok na ladenie problémov s prenosom siete.

Najprv som spustil PsPing na Alvinov port TCP. Použil som predvolené nastavenia - nič zvláštne. Z viac ako tisícky pingov ani jeden neprekročil 10 ms, s výnimkou prvého na zahriatie. To je v rozpore s pozorovaným zvýšením latencie o 50 ms na 99. percentile: tam by sme na každých 100 požiadaviek mali vidieť približne jednu požiadavku s latenciou 50 ms.

Potom som to skúsil tracert: Môže nastať problém v jednom z uzlov na trase medzi Alvinom a klientom. Ale aj stopár sa vrátil naprázdno.

Takže to nebol môj kód, implementácia gRPC alebo sieť, ktorá spôsobila oneskorenie. Začínal som sa báť, že to nikdy nepochopím.

Teraz na akom OS sme

gRPC široko používaný na Linuxe, ale exotický na Windows. Rozhodol som sa vyskúšať experiment, ktorý fungoval: vytvoril som virtuálny stroj Linux, skompiloval som Alvin pre Linux a nasadil som ho.

Niekedy je viac menej. Zníženie záťaže má za následok zvýšenie latencie

A tu je to, čo sa stalo: Linuxový pingpongový server nemal rovnaké oneskorenia ako podobný hostiteľ Windows, hoci zdroj údajov sa nelíšil. Ukazuje sa, že problém je v implementácii gRPC pre Windows.

Nagleho algoritmus

Celý ten čas som si myslel, že mi chýba vlajka gRPC. Teraz chápem, čo to naozaj je gRPC Vlajka systému Windows chýba. Našiel som internú knižnicu RPC, o ktorej som si bol istý, že bude dobre fungovať pre všetky nastavené príznaky Winsock. Potom som pridal všetky tieto príznaky do gRPC a nasadil Alvin na Windows, v opravenom Windows ping-pong serveri!

Niekedy je viac menej. Zníženie záťaže má za následok zvýšenie latencie

takmer Hotovo: Začal som odstraňovať pridané príznaky jeden po druhom, kým sa nevrátila regresia, aby som mohol určiť príčinu. Bolo to neslávne známe TCP_NODELAY, Prepínač Naglovho algoritmu.

Nagleho algoritmus sa pokúša znížiť počet paketov odoslaných cez sieť oneskorením prenosu správ, kým veľkosť paketu nepresiahne určitý počet bajtov. Aj keď to môže byť príjemné pre priemerného používateľa, je to deštruktívne pre servery v reálnom čase, pretože operačný systém oneskorí niektoré správy, čo spôsobí oneskorenia pri nízkej QPS. U gRPC tento príznak bol nastavený v implementácii Linuxu pre TCP sockety, ale nie vo Windows. Toto som ja opravené.

Záver

Vyššia latencia pri nízkej QPS bola spôsobená optimalizáciou OS. Pri spätnom pohľade profilovanie nezistilo latenciu, pretože to bolo vykonané v režime jadra, a nie v režime užívateľský režim. Neviem, či sa dá Nagleho algoritmus pozorovať pomocou ETW zachytení, ale bolo by to zaujímavé.

Pokiaľ ide o experiment localhost, pravdepodobne sa nedotkol skutočného sieťového kódu a algoritmus Nagle sa nespustil, takže problémy s latenciou zmizli, keď sa klient dostal k Alvinovi cez localhost.

Keď nabudúce uvidíte zvýšenie latencie, keď počet žiadostí za sekundu klesne, Nagleho algoritmus by mal byť na vašom zozname podozrivých!

Zdroj: hab.com

Pridať komentár