Som i
En dag våknet jeg til en misfornøyd e-post på grunn av lange forsinkelser med Alvin, som vi planla å lansere i nær fremtid. Nærmere bestemt opplevde klienten 99. persentil latens i området 50 ms, godt over latensbudsjettet vårt. Dette var overraskende da jeg testet tjenesten mye, spesielt på latens, som er en vanlig klage.
Før jeg satte Alvin i testing, kjørte jeg mange eksperimenter med 40 10 spørringer per sekund (QPS), alle viste ventetid på mindre enn 40 ms. Jeg var klar til å erklære at jeg ikke var enig i resultatene deres. Men ved å se på brevet igjen, la jeg merke til noe nytt: Jeg hadde ikke akkurat testet forholdene de nevnte, deres QPS var mye lavere enn min. Jeg testet på 1k QPS, men de bare på XNUMXk. Jeg kjørte et nytt eksperiment, denne gangen med lavere QPS, bare for å blidgjøre dem.
Siden jeg blogger om dette, har du sikkert allerede skjønt at tallene deres stemte. Jeg testet den virtuelle klienten min om og om igjen, med samme resultat: et lavt antall forespørsler øker ikke bare ventetiden, men øker antallet forespørsler med en ventetid på mer enn 10 ms. Med andre ord, hvis ca. 40 forespørsler per sekund ved 50k QPS oversteg 50 ms, så var det ved 1k QPS 100 forespørsler over 50 ms hvert sekund. Paradoks!
Begrenser søket
Når du står overfor et latensproblem i et distribuert system med mange komponenter, er det første trinnet å lage en kort liste over mistenkte. La oss grave litt dypere inn i Alvins arkitektur:
Et godt utgangspunkt er en liste over fullførte I/O-overganger (nettverksanrop/diskoppslag osv.). La oss prøve å finne ut hvor forsinkelsen er. Foruten den åpenbare I/O med klienten, tar Alvin et ekstra skritt: han får tilgang til datalageret. Denne lagringen opererer imidlertid i samme klynge som Alvin, så latensen der bør være mindre enn hos klienten. Så listen over mistenkte:
- Nettverksanrop fra klient til Alvin.
- Nettverksanrop fra Alvin til datalageret.
- Søk på disk i datalageret.
- Nettverksanrop fra datavarehuset til Alvin.
- Nettverksanrop fra Alvin til en klient.
La oss prøve å stryke ut noen punkter.
Datalagring har ingenting med det å gjøre
Det første jeg gjorde var å konvertere Alvin til en ping-ping-server som ikke behandler forespørsler. Når den mottar en forespørsel, returnerer den et tomt svar. Hvis ventetiden reduseres, er en feil i implementeringen av Alvin eller datavarehus ikke noe uhørt. I det første eksperimentet får vi følgende graf:
Som du kan se, er det ingen forbedring når du bruker ping-ping-serveren. Dette betyr at datavarehuset ikke øker ventetiden, og listen over mistenkte er halvert:
- Nettverksanrop fra klient til Alvin.
- Nettverksanrop fra Alvin til en klient.
Flott! Listen krymper raskt. Jeg trodde jeg nesten hadde skjønt årsaken.
gRPC
Nå er det på tide å introdusere deg for en ny spiller: gRPC
godt optimert og mye brukt, dette var første gang jeg brukte det på et system av denne størrelsen, og jeg forventet at implementeringen min skulle være suboptimal - for å si det mildt.
tilgjengelighet gRPC
i stabelen ga opphav til et nytt spørsmål: kanskje det er min implementering eller meg selv gRPC
forårsaker latensproblem? Legge til en ny mistenkt på listen:
- Kunden ringer biblioteket
gRPC
- Bibliotek
gRPC
foretar et nettverksanrop til biblioteket på klientengRPC
på server - Bibliotek
gRPC
kontakter Alvin (ingen operasjon ved ping-pong-server)
For å gi deg en ide om hvordan koden ser ut, er ikke min klient/Alvin-implementering mye forskjellig fra klient-serveren.
Merk: Listen ovenfor er litt forenklet fordi
gRPC
gjør det mulig å bruke din egen (mal?) trådmodell, der utførelsesstabelen er flettet sammengRPC
og brukerimplementering. For enkelhets skyld vil vi holde oss til denne modellen.
Profilering vil fikse alt
Etter å ha krysset over datalagrene trodde jeg at jeg nesten var ferdig: «Nå er det enkelt! La oss bruke profilen og finne ut hvor forsinkelsen oppstår." Jeg
Jeg tok fire profiler: med høy QPS (lav latency) og med en ping-pong-server med lav QPS (høy latency), både på klientsiden og på serversiden. Og for sikkerhets skyld tok jeg også en prøveprosessorprofil. Når jeg sammenligner profiler, ser jeg vanligvis etter en unormal anropsstabel. For eksempel, på den dårlige siden med høy latens er det mange flere kontekstbrytere (10 ganger eller mer). Men i mitt tilfelle var antallet kontekstbrytere nesten det samme. Til min skrekk var det ikke noe vesentlig der.
Ytterligere feilsøking
Jeg var desperat. Jeg visste ikke hvilke andre verktøy jeg kunne bruke, og min neste plan var i hovedsak å gjenta eksperimentene med forskjellige varianter i stedet for å tydelig diagnostisere problemet.
Hva om
Helt fra begynnelsen var jeg bekymret for den spesifikke 50ms latensen. Dette er en veldig stor tid. Jeg bestemte meg for at jeg ville kutte biter ut av koden til jeg kunne finne ut nøyaktig hvilken del som forårsaket denne feilen. Så kom et eksperiment som fungerte.
Som vanlig ser det i ettertid ut til at alt var åpenbart. Jeg plasserte klienten på samme maskin som Alvin - og sendte en forespørsel til localhost
. Og økningen i latens er borte!
Noe var galt med nettverket.
Lære ferdigheter i nettverksingeniører
Jeg må innrømme: kunnskapen min om nettverksteknologi er forferdelig, spesielt med tanke på at jeg jobber med dem hver dag. Men nettverket var hovedmistenkt, og jeg trengte å lære å feilsøke det.
Heldigvis elsker Internett de som vil lære. Kombinasjonen av ping og tracert virket som en god nok start på feilsøking av nettverkstransportproblemer.
Først lanserte jeg
Så prøvde jeg
Så det var ikke koden min, gRPC-implementeringen eller nettverket som forårsaket forsinkelsen. Jeg begynte å bekymre meg for at jeg aldri ville forstå dette.
Hvilket OS har vi nå
gRPC
mye brukt på Linux, men eksotisk på Windows. Jeg bestemte meg for å prøve et eksperiment, som fungerte: Jeg opprettet en virtuell Linux-maskin, kompilerte Alvin for Linux og implementerte den.
Og her er hva som skjedde: Linux ping-pong-serveren hadde ikke de samme forsinkelsene som en lignende Windows-vert, selv om datakilden ikke var annerledes. Det viser seg at problemet ligger i gRPC-implementeringen for Windows.
Nagles algoritme
Hele denne tiden trodde jeg at jeg manglet et flagg gRPC
. Nå forstår jeg hva det egentlig er gRPC
Windows-flagg mangler. Jeg fant et internt RPC-bibliotek som jeg var sikker på ville fungere bra for alle flaggsett
nesten Ferdig: Jeg begynte å fjerne de lagte flaggene ett om gangen til regresjonen kom tilbake slik at jeg kunne finne årsaken. Det var beryktet
gRPC
dette flagget ble satt i Linux-implementeringen for TCP-sockets, men ikke i Windows. Jeg er denne
Konklusjon
Den høyere latensen ved lav QPS ble forårsaket av OS-optimalisering. I ettertid oppdaget ikke profilering ventetid fordi det ble gjort i kjernemodus i stedet for i
Når det gjelder localhost-eksperimentet, rørte det sannsynligvis ikke selve nettverkskoden og Nagles algoritme kjørte ikke, så latensproblemene forsvant da klienten nådde Alvin gjennom localhost.
Neste gang du ser en økning i ventetiden ettersom antall forespørsler per sekund reduseres, bør Nagles algoritme være på listen over mistenkte!
Kilde: www.habr.com