Včasih je več manj. Ko zmanjšanje obremenitve povzroči večjo zakasnitev

Kot v večina objav, obstaja težava s porazdeljeno storitvijo, poimenujmo to storitev Alvin. Tokrat težave nisem odkril sam, sporočili so mi fantje s strani naročnika.

Nekega dne sem se zbudil z nezadovoljnim e-poštnim sporočilom zaradi dolgih zamud z Alvinom, ki smo ga načrtovali lansirati v bližnji prihodnosti. Natančneje, odjemalec je doživel zakasnitev 99. percentila v območju 50 ms, kar je precej nad našim proračunom za zakasnitev. To je bilo presenetljivo, saj sem storitev obsežno testiral, zlasti glede zakasnitve, kar je pogosta pritožba.

Preden sem dal Alvina v testiranje, sem izvedel veliko poskusov s 40 poizvedbami na sekundo (QPS), pri čemer so vsi pokazali zakasnitev manj kot 10 ms. Bil sem pripravljen izjaviti, da se ne strinjam z njihovimi rezultati. Ko pa sem ponovno pogledal pismo, sem opazil nekaj novega: nisem natančno testiral pogojev, ki so jih omenili, njihov QPS je bil precej nižji od mojega. Testiral sem pri 40k QPS, oni pa le pri 1k. Izvedel sem še en poskus, tokrat z nižjim QPS, samo da bi jih pomiril.

Ker pišem blog o tem, ste verjetno že ugotovili, da so bile njihove številke pravilne. Svojega virtualnega odjemalca sem testiral znova in znova z enakim rezultatom: majhno število zahtev ne le poveča zakasnitev, ampak poveča število zahtev z zakasnitvijo več kot 10 ms. Z drugimi besedami, če je pri 40k QPS približno 50 zahtev na sekundo preseglo 50 ms, potem je bilo pri 1k QPS vsako sekundo 100 zahtev nad 50 ms. Paradoks!

Včasih je več manj. Ko zmanjšanje obremenitve povzroči večjo zakasnitev

Zoženje iskanja

Ko se soočite s težavo z zakasnitvijo v porazdeljenem sistemu z veliko komponentami, je prvi korak ustvariti kratek seznam osumljencev. Poglobimo se nekoliko globlje v Alvinovo arhitekturo:

Včasih je več manj. Ko zmanjšanje obremenitve povzroči večjo zakasnitev

Dobro izhodišče je seznam dokončanih V/I prehodov (omrežni klici/iskanje diska itd.). Poskusimo ugotoviti, kje je zamuda. Poleg očitnega V/I z odjemalcem Alvin naredi dodaten korak: dostopa do shrambe podatkov. Vendar pa ta shramba deluje v isti gruči kot Alvin, zato bi morala biti tam zakasnitev manjša kot pri odjemalcu. Torej, seznam osumljencev:

  1. Omrežni klic od stranke do Alvina.
  2. Omrežni klic Alvina v shrambo podatkov.
  3. Iskanje na disku v shrambi podatkov.
  4. Omrežni klic iz podatkovnega skladišča Alvinu.
  5. Omrežni klic Alvina stranki.

Poskusimo nekaj točk prečrtati.

Shranjevanje podatkov nima nič s tem

Prva stvar, ki sem jo naredil, je bila pretvorba Alvina v strežnik ping-ping, ki ne obdeluje zahtev. Ko prejme zahtevo, vrne prazen odgovor. Če se zakasnitev zmanjša, potem napaka v implementaciji Alvina ali podatkovnega skladišča ni nič nezaslišanega. V prvem poskusu dobimo naslednji graf:

Včasih je več manj. Ko zmanjšanje obremenitve povzroči večjo zakasnitev

Kot lahko vidite, pri uporabi strežnika ping-ping ni izboljšav. To pomeni, da podatkovno skladišče ne poveča zakasnitve, seznam osumljencev pa se prepolovi:

  1. Omrežni klic od stranke do Alvina.
  2. Omrežni klic Alvina stranki.

Super! Seznam se hitro krči. Mislil sem, da sem skoraj ugotovil razlog.

gRPC

Zdaj je čas, da vam predstavim novega igralca: gRPC. To je Googlova odprtokodna knjižnica za komunikacijo med procesom RPC... Čeprav gRPC dobro optimiziran in pogosto uporabljen, to je bilo prvič, da sem ga uporabljal v sistemu te velikosti in pričakoval sem, da bo moja izvedba neoptimalna - milo rečeno.

razpoložljivost gRPC v skladu je povzročil novo vprašanje: morda je to moja implementacija ali jaz gRPC povzroča težave z zakasnitvijo? Dodajanje novega osumljenca na seznam:

  1. Naročnik pokliče knjižnico gRPC
  2. Knjižnica gRPC opravi omrežni klic v knjižnico na odjemalcu gRPC na strežniku
  3. Knjižnica gRPC stiki Alvin (brez delovanja v primeru strežnika za ping-pong)

Da bi vam predstavili, kako izgleda koda, moja implementacija odjemalec/Alvin ni veliko drugačna od implementacije odjemalec-strežnik asinhroni primeri.

Opomba: zgornji seznam je nekoliko poenostavljen, ker gRPC omogoča uporabo lastnega (predloga?) modela navojev, v katerem je izvajalni sklad prepleten gRPC in uporabniška implementacija. Zaradi enostavnosti se bomo držali tega modela.

Profiliranje bo vse popravilo

Ko sem prečrtal shrambo podatkov, sem mislil, da sem skoraj končal: »Zdaj je enostavno! Uporabimo profil in ugotovimo, kje prihaja do zamude.« jaz velik ljubitelj natančnega profiliranja, ker so procesorji zelo hitri in večinoma niso ozko grlo. Večina zamud se pojavi, ko mora procesor ustaviti obdelavo, da naredi nekaj drugega. Accurate CPE Profiling naredi prav to: natančno beleži vse kontekstna stikala in pojasnjuje, kje prihaja do zamud.

Vzel sem štiri profile: z visokim QPS (nizka zakasnitev) in s strežnikom za ping-pong z nizkim QPS (visoka zakasnitev), tako na strani odjemalca kot na strani strežnika. In za vsak slučaj sem vzel tudi vzorčni profil procesorja. Ko primerjam profile, običajno iščem neobičajen sklad klicev. Na primer, na slabi strani z visoko zakasnitvijo je veliko več preklopov konteksta (10-krat ali več). Toda v mojem primeru je bilo število preklopov konteksta skoraj enako. Na mojo grozo tam ni bilo nič pomembnega.

Dodatno odpravljanje napak

Bil sem obupan. Nisem vedel, katera druga orodja bi lahko uporabil, in moj naslednji načrt je bil v bistvu ponoviti poskuse z različnimi različicami, namesto da bi jasno diagnosticiral težavo.

Kaj če

Že od samega začetka me je skrbela specifična zakasnitev 50 ms. To je zelo velik čas. Odločil sem se, da bom iz kode izrezal dele, dokler ne bom natančno ugotovil, kateri del povzroča to napako. Potem je prišel poskus, ki je deloval.

Kot ponavadi se za nazaj zdi, da je bilo vse očitno. Odjemalca sem postavil na isti stroj kot Alvin - in poslal zahtevo na localhost. In povečanja zakasnitve ni več!

Včasih je več manj. Ko zmanjšanje obremenitve povzroči večjo zakasnitev

Nekaj ​​je bilo narobe z omrežjem.

Učenje veščin omrežnega inženirja

Moram priznati: moje znanje o omrežnih tehnologijah je grozljivo, sploh glede na to, da z njimi delam vsak dan. Toda omrežje je bilo glavni osumljenec in moral sem se naučiti, kako ga odpraviti.

Na srečo ima internet rad tiste, ki se želijo učiti. Kombinacija pinga in tracerta se je zdela dovolj dober začetek za odpravljanje težav pri omrežnem transportu.

Najprej sem zagnal PsPing na Alvinova vrata TCP. Uporabil sem privzete nastavitve - nič posebnega. Od več kot tisoč pingov noben ni presegel 10 ms, z izjemo prvega za ogrevanje. To je v nasprotju z opaženim povečanjem zakasnitve za 50 ms pri 99. percentilu: tam bi morali za vsakih 100 zahtev videti približno eno zahtevo z zakasnitvijo 50 ms.

Potem sem poskusil tracert: Morda je težava na enem od vozlišč na poti med Alvinom in stranko. A tudi traser se je vrnil praznih rok.

Zakasnitev torej ni povzročala moja koda, izvedba gRPC ali omrežje. Začelo me je skrbeti, da tega ne bom nikoli razumel.

Zdaj pa na katerem OS-u smo

gRPC široko uporabljen v Linuxu, a eksotičen v sistemu Windows. Odločil sem se preizkusiti eksperiment, ki je deloval: ustvaril sem virtualni stroj Linux, prevedel Alvina za Linux in ga namestil.

Včasih je več manj. Ko zmanjšanje obremenitve povzroči večjo zakasnitev

In to se je zgodilo: strežnik za namizni tenis Linux ni imel enakih zamud kot podoben gostitelj Windows, čeprav vir podatkov ni bil nič drugačen. Izkazalo se je, da je težava v implementaciji gRPC za Windows.

Naglov algoritem

Ves ta čas sem mislil, da pogrešam zastavo gRPC. Zdaj razumem, kaj v resnici je gRPC Windows zastavica manjka. Našel sem notranjo knjižnico RPC, za katero sem bil prepričan, da bo dobro delovala za vse nastavljene zastavice Winsock. Nato sem dodal vse te zastavice v gRPC in namestil Alvina v sistemu Windows, v popravljenem strežniku Windows za ping-pong!

Včasih je več manj. Ko zmanjšanje obremenitve povzroči večjo zakasnitev

Skoraj Končano: dodane zastavice sem začel odstranjevati eno za drugo, dokler se ni vrnila regresija, da sem lahko natančno določil vzrok. Bilo je zloglasno TCP_NODELAY, stikalo Naglovega algoritma.

Naglov algoritem poskuša zmanjšati število paketov, poslanih po omrežju, tako da odloži prenos sporočil, dokler velikost paketa ne preseže določenega števila bajtov. Čeprav je to lahko prijetno za povprečnega uporabnika, je uničujoče za strežnike v realnem času, saj bo OS zakasnil nekatera sporočila, kar bo povzročilo zakasnitve pri nizkih QPS. U gRPC ta zastavica je bila nastavljena v izvedbi Linuxa za vtičnice TCP, ne pa tudi v sistemu Windows. Jaz sem to popravljeno.

Zaključek

Večjo zakasnitev pri nizkih QPS je povzročila optimizacija OS. V retrospektivi profiliranje ni zaznalo zakasnitve, ker je bilo izvedeno v načinu jedra in ne v uporabniški način. Ne vem, ali je mogoče Naglov algoritem opazovati prek zajemov ETW, vendar bi bilo zanimivo.

Kar se tiče eksperimenta z lokalnim gostiteljem, se verjetno ni dotaknil dejanske omrežne kode in Naglov algoritem se ni zagnal, tako da so težave z zakasnitvijo izginile, ko je odjemalec dosegel Alvina prek lokalnega gostitelja.

Naslednjič, ko opazite povečanje zakasnitve, ko se število zahtev na sekundo zmanjša, bi moral biti Naglov algoritem na vašem seznamu osumljencev!

Vir: www.habr.com

Dodaj komentar