Někdy je více méně. Při snižování zátěže dochází ke zvýšení latence

Stejně jako v většina příspěvků, je problém s distribuovanou službou, říkejme této službě Alvin. Tentokrát jsem problém neobjevil sám, informovali mě kluci ze strany klienta.

Jednoho dne mě probudil nespokojený email kvůli dlouhým prodlevám s Alvinem, který jsme plánovali spustit v blízké budoucnosti. Konkrétně klient zaznamenal latenci 99. percentilu v oblasti 50 ms, což je výrazně nad naším rozpočtem na latenci. To bylo překvapivé, protože jsem službu rozsáhle testoval, zejména na latenci, což je běžná stížnost.

Než jsem dal Alvina do testování, provedl jsem spoustu experimentů se 40 10 dotazy za sekundu (QPS), přičemž všechny vykazovaly latenci menší než 40 ms. Byl jsem připraven prohlásit, že s jejich výsledky nesouhlasím. Ale při dalším pohledu na dopis jsem si všiml něčeho nového: podmínky, které zmiňovali, jsem přesně netestoval, jejich QPS byla mnohem nižší než moje. Testoval jsem na 1k QPS, ale oni jen na XNUMXk. Provedl jsem další experiment, tentokrát s nižším QPS, jen abych je uklidnil.

Vzhledem k tomu, že o tom píšu blog, pravděpodobně jste již přišli na to, že jejich čísla byla správná. Svého virtuálního klienta jsem testoval znovu a znovu, se stejným výsledkem: nízký počet požadavků nejen zvyšuje latenci, ale zvyšuje počet požadavků s latencí více než 10 ms. Jinými slovy, pokud při 40k QPS asi 50 požadavků za sekundu přesáhlo 50 ms, pak při 1k QPS bylo každou sekundu 100 požadavků nad 50 ms. Paradox!

Někdy je více méně. Při snižování zátěže dochází ke zvýšení latence

Zúžení vyhledávání

Když čelíte problému s latencí v distribuovaném systému s mnoha komponentami, prvním krokem je vytvořit krátký seznam podezřelých. Pojďme se ponořit trochu hlouběji do Alvinovy ​​architektury:

Někdy je více méně. Při snižování zátěže dochází ke zvýšení latence

Dobrým výchozím bodem je seznam dokončených I/O přechodů (síťová volání/vyhledávání disku atd.). Zkusme zjistit, kde je zpoždění. Kromě samozřejmého I/O s klientem podnikne Alvin další krok: přistoupí k datovému úložišti. Toto úložiště však funguje ve stejném clusteru jako Alvin, takže latence by tam měla být menší než u klienta. Takže seznam podezřelých:

  1. Síťový hovor od klienta k Alvinovi.
  2. Síťové volání od Alvina do úložiště dat.
  3. Vyhledejte na disku v úložišti dat.
  4. Síťové volání z datového skladu do Alvina.
  5. Síťový hovor od Alvina ke klientovi.

Zkusme si odškrtnout některé body.

Ukládání dat s tím nemá nic společného

První věc, kterou jsem udělal, bylo převést Alvina na ping-ping server, který nezpracovává požadavky. Když obdrží požadavek, vrátí prázdnou odpověď. Pokud se latence sníží, pak chyba v implementaci Alvin nebo datového skladu není nic neslýchaného. V prvním experimentu dostaneme následující graf:

Někdy je více méně. Při snižování zátěže dochází ke zvýšení latence

Jak vidíte, při používání ping-ping serveru nedochází k žádnému zlepšení. To znamená, že datový sklad nezvyšuje latenci a seznam podezřelých se zkrátí na polovinu:

  1. Síťový hovor od klienta k Alvinovi.
  2. Síťový hovor od Alvina ke klientovi.

Skvělý! Seznam se rychle zmenšuje. Myslel jsem, že jsem skoro přišel na důvod.

gRPC

Nyní je čas představit vám nového hráče: gRPC. Toto je knihovna s otevřeným zdrojovým kódem od společnosti Google pro komunikaci v průběhu procesu RPC, ačkoli gRPC dobře optimalizovaný a široce používaný, bylo to poprvé, co jsem jej použil na systému této velikosti, a očekával jsem, že moje implementace bude přinejmenším suboptimální.

dostupnost gRPC v zásobníku vyvolala novou otázku: možná je to moje implementace nebo já gRPC způsobuje problém s latencí? Přidání nového podezřelého na seznam:

  1. Klient zavolá do knihovny gRPC
  2. knihovna gRPC provede síťové volání do knihovny na klientovi gRPC na serveru
  3. knihovna gRPC kontaktuje Alvina (žádná operace v případě ping-pong serveru)

Abyste měli představu o tom, jak kód vypadá, moje implementace klient/Alvin se příliš neliší od implementace klient-server asynchronní příklady.

Poznámka: Výše ​​uvedený seznam je trochu zjednodušený, protože gRPC umožňuje použít vlastní (šablonový?) model vláken, ve kterém je propleten zásobník provádění gRPC a uživatelská implementace. Pro jednoduchost se budeme držet tohoto modelu.

Profilování vše napraví

Po přeškrtnutí datových úložišť jsem si myslel, že jsem téměř hotový: „Teď je to snadné! Aplikujme profil a zjistíme, kde dochází ke zpoždění.“ já velký fanoušek přesného profilování, protože CPU jsou velmi rychlé a většinou nejsou úzkým hrdlem. K většině zpoždění dochází, když procesor musí zastavit zpracování, aby mohl udělat něco jiného. Přesné profilování CPU to dělá: přesně zaznamenává vše kontextové přepínače a dává jasně najevo, kde dochází ke zpožděním.

Vzal jsem čtyři profily: s vysokým QPS (nízká latence) a s ping-pongovým serverem s nízkou QPS (vysoká latence), a to jak na straně klienta, tak na straně serveru. A pro každý případ jsem vzal i ukázkový profil procesoru. Při porovnávání profilů obvykle hledám anomální zásobník hovorů. Například na špatné straně s vysokou latencí existuje mnohem více kontextových přepínačů (10krát nebo více). Ale v mém případě byl počet kontextových přepínačů téměř stejný. K mému zděšení tam nebylo nic významného.

Další ladění

Byl jsem zoufalý. Nevěděl jsem, jaké další nástroje bych mohl použít, a mým dalším plánem bylo v podstatě opakovat experimenty s různými variantami, spíše než jasně diagnostikovat problém.

Co když

Od samého začátku jsem měl obavy z konkrétní 50ms latence. Tohle je hodně velká doba. Rozhodl jsem se, že z kódu vyřežu kousky, dokud přesně nezjistím, která část tuto chybu způsobuje. Pak přišel experiment, který fungoval.

Jako obvykle, při zpětném pohledu se zdá, že vše bylo zřejmé. Umístil jsem klienta na stejný stroj jako Alvin - a poslal jsem mu požadavek localhost. A nárůst latence je pryč!

Někdy je více méně. Při snižování zátěže dochází ke zvýšení latence

Něco bylo špatně se sítí.

Naučte se dovednosti síťového inženýra

Musím přiznat: moje znalosti síťových technologií jsou hrozné, zvláště když s nimi pracuji každý den. Ale síť byla hlavním podezřelým a potřeboval jsem se naučit, jak ji odladit.

Naštěstí internet miluje ty, kteří se chtějí učit. Kombinace pingu a tracert se zdála jako dostatečně dobrý začátek k ladění problémů s přenosem v síti.

Nejprve jsem spustil PsPing na Alvinův TCP port. Použil jsem výchozí nastavení - nic zvláštního. Z více než tisíce pingů žádný nepřesáhl 10 ms, s výjimkou prvního pro zahřátí. To je v rozporu s pozorovaným zvýšením latence o 50 ms na 99. percentilu: tam bychom na každých 100 požadavků měli vidět asi jeden požadavek s latencí 50 ms.

Pak jsem to zkusil tracert: Může nastat problém v jednom z uzlů na trase mezi Alvinem a klientem. Stopař se ale také vrátil s prázdnou.

Takže to nebyl můj kód, implementace gRPC nebo síť, která způsobila zpoždění. Začínal jsem se bát, že tohle nikdy nepochopím.

A teď na jakém OS jsme

gRPC široce používaný na Linuxu, ale exotický na Windows. Rozhodl jsem se vyzkoušet experiment, který fungoval: vytvořil jsem virtuální stroj Linux, zkompiloval Alvin pro Linux a nasadil jej.

Někdy je více méně. Při snižování zátěže dochází ke zvýšení latence

A stalo se toto: Linuxový ping-pongový server neměl stejné zpoždění jako podobný hostitel Windows, ačkoli zdroj dat se nelišil. Ukazuje se, že problém je v implementaci gRPC pro Windows.

Nagleho algoritmus

Celou tu dobu jsem si myslel, že mi chybí vlajka gRPC. Teď už chápu, co to vlastně je gRPC Chybí vlajka Windows. Našel jsem interní knihovnu RPC, o které jsem si byl jistý, že bude dobře fungovat pro všechny nastavené příznaky Winsock. Pak jsem přidal všechny tyto příznaky do gRPC a nasadil Alvin na Windows, v opraveném Windows ping-pong serveru!

Někdy je více méně. Při snižování zátěže dochází ke zvýšení latence

Téměř Hotovo: Začal jsem odstraňovat přidané příznaky jeden po druhém, dokud se regrese nevrátila, abych mohl určit příčinu. Bylo to neslavné TCP_NODELAY, Přepínač Nagleho algoritmu.

Nagleho algoritmus se pokouší snížit počet paketů odesílaných přes síť zpožděním přenosu zpráv, dokud velikost paketu nepřekročí určitý počet bajtů. I když to může být příjemné pro průměrného uživatele, je to destruktivní pro servery v reálném čase, protože OS zpozdí některé zprávy, což způsobí zpoždění při nízké QPS. U gRPC tento příznak byl nastaven v implementaci Linuxu pro TCP sockety, ale ne ve Windows. já jsem tohle opraveno.

Závěr

Vyšší latence při nízké QPS byla způsobena optimalizací OS. Při zpětném pohledu profilování nezjistilo latenci, protože bylo provedeno v režimu jádra, nikoli v režimu uživatelský režim. Nevím, jestli lze Nagleův algoritmus pozorovat pomocí ETW záchytů, ale bylo by to zajímavé.

Pokud jde o experiment localhost, pravděpodobně se nedotkl skutečného síťového kódu a algoritmus Nagle neběžel, takže problémy s latencí zmizely, když klient dosáhl Alvina přes localhost.

Až příště uvidíte nárůst latence, protože počet požadavků za sekundu klesá, měl by být Nagleův algoritmus na vašem seznamu podezřelých!

Zdroj: www.habr.com

Přidat komentář