SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Ydelsesanalyse og justering er et kraftfuldt værktøj til at verificere præstationsoverholdelse for kunder.

Ydeevneanalyse kan bruges til at kontrollere for flaskehalse i et program ved at anvende en videnskabelig tilgang til at teste tuning-eksperimenter. Denne artikel definerer en generel tilgang til ydelsesanalyse og tuning ved at bruge en Go-webserver som eksempel.

Go er især god her, fordi den har profileringsværktøjer pprof i standardbiblioteket.

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

strategi

Lad os oprette en opsummerende liste til vores strukturelle analyse. Vi vil forsøge at bruge nogle data til at træffe beslutninger i stedet for at foretage ændringer baseret på intuition eller gætværk. For at gøre dette vil vi gøre dette:

  • Vi fastlægger optimeringsgrænserne (krav);
  • Vi beregner transaktionsbelastningen for systemet;
  • Vi udfører testen (opretter data);
  • Vi observerer;
  • Vi analyserer – er alle krav opfyldt?
  • Vi opstiller det videnskabeligt, laver en hypotese;
  • Vi udfører et eksperiment for at teste denne hypotese.

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Simpel HTTP-serverarkitektur

Til denne artikel vil vi bruge en lille HTTP-server i Golang. Al kode fra denne artikel kan findes her.

Applikationen, der analyseres, er en HTTP-server, der poller Postgresql for hver anmodning. Derudover er der Prometheus, node_exporter og Grafana til indsamling og visning af applikations- og systemmetrikker.

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

For at forenkle overvejer vi, at hver tjeneste og database til horisontal skalering (og forenkling af beregninger) implementeres sammen:

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

At definere mål

På dette trin beslutter vi os for målet. Hvad prøver vi at analysere? Hvordan ved vi, hvornår det er tid til at slutte? I denne artikel vil vi forestille os, at vi har kunder, og at vores service vil behandle 10 anmodninger i sekundet.

В Google SRE-bog Metoder til udvælgelse og modellering diskuteres i detaljer. Lad os gøre det samme og bygge modeller:

  • Latency: 99 % af anmodningerne skal udføres på mindre end 60 ms;
  • Omkostninger: Tjenesten skal forbruge det minimumsbeløb, som vi mener er rimeligt muligt. For at gøre dette maksimerer vi gennemløbet;
  • Kapacitetsplanlægning: Kræver forståelse og dokumentation af, hvor mange forekomster af applikationen, der skal køres, inklusive overordnet skaleringsfunktionalitet, og hvor mange forekomster, der er nødvendige for at opfylde krav til indledende indlæsning og klargøring redundans n+1.

Latency kan kræve optimering ud over analyse, men gennemløbet skal klart analyseres. Når du bruger SRE SLO-processen, kommer anmodningen om forsinkelse fra kunden eller virksomheden, repræsenteret af produktets ejer. Og vores service vil opfylde denne forpligtelse helt fra begyndelsen uden nogen indstillinger!

Opsætning af et testmiljø

Ved hjælp af et testmiljø vil vi være i stand til at placere en målt belastning på vores system. Til analyse vil der blive genereret data om ydeevnen af ​​webtjenesten.

Transaktionsbelastning

Dette miljø bruger vegetere for at oprette en tilpasset HTTP-anmodningshastighed, indtil den stoppes:

$ make load-test LOAD_TEST_RATE=50
echo "POST http://localhost:8080" | vegeta attack -body tests/fixtures/age_no_match.json -rate=50 -duration=0 | tee results.bin | vegeta report

ser

Transaktionsbelastning vil blive påført under kørsel. Ud over applikations- (antal anmodninger, svarforsinkelse) og operativsystem (hukommelse, CPU, IOPS) metrics, vil applikationsprofilering blive kørt for at forstå, hvor det har problemer, og hvordan CPU-tid forbruges.

Profilering

Profilering er en type måling, der giver dig mulighed for at se, hvor CPU-tiden går hen, når en applikation kører. Det giver dig mulighed for at bestemme præcis, hvor og hvor meget processortid der bruges:

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Disse data kan bruges under analyse til at få indsigt i spildt CPU-tid og unødvendigt arbejde, der udføres. Go (pprof) kan generere profiler og visualisere dem som flammegrafer ved hjælp af et standard sæt værktøjer. Jeg vil tale om deres brug og opsætningsvejledning senere i artiklen.

Udførelse, observation, analyse.

Lad os lave et eksperiment. Vi vil udføre, observere og analysere, indtil vi er tilfredse med præstationen. Lad os vælge en vilkårligt lav belastningsværdi for at anvende den for at opnå resultaterne af de første observationer. Ved hvert efterfølgende trin vil vi øge belastningen med en vis skaleringsfaktor, valgt med en vis variation. Hver belastningstestkørsel udføres med antallet af anmodninger justeret: make load-test LOAD_TEST_RATE=X.

50 anmodninger i sekundet

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Vær opmærksom på de to øverste grafer. Øverst til venstre viser vores ansøgning, at vores ansøgning behandler 50 anmodninger i sekundet (mener det), og øverst til højre viser varigheden af ​​hver anmodning. Begge parametre hjælper os med at se og analysere, om vi er inden for vores præstationsgrænser eller ej. Rød linje på grafen HTTP-anmodningsforsinkelse viser SLO på 60ms. Linjen viser, at vi ligger et godt stykke under vores maksimale svartid.

Lad os se på omkostningssiden:

10000 anmodninger pr. sekund / 50 anmodninger pr. server = 200 servere + 1

Vi kan stadig forbedre dette tal.

500 anmodninger i sekundet

Flere interessante ting begynder at ske, når belastningen når op på 500 anmodninger i sekundet:

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Igen, i grafen øverst til venstre kan du se, at applikationen registrerer normal belastning. Hvis dette ikke er tilfældet, er der et problem på den server, som applikationen kører på. Svarforsinkelsesgrafen er placeret øverst til højre og viser, at 500 anmodninger i sekundet resulterede i en svarforsinkelse på 25-40ms. 99. percentilen passer stadig fint ind i 60ms SLO valgt ovenfor.

Med hensyn til omkostninger:

10000 anmodninger pr. sekund / 500 anmodninger pr. server = 20 servere + 1

Alt kan stadig forbedres.

1000 anmodninger i sekundet

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Fantastisk lancering! Applikationen viser, at den behandlede 1000 anmodninger i sekundet, men latensgrænsen blev overtrådt af SLO'en. Dette kan ses på linje p99 i grafen øverst til højre. På trods af at p100-linjen er meget højere, er de faktiske forsinkelser højere end det maksimale på 60ms. Lad os dykke ned i profilering for at finde ud af, hvad applikationen rent faktisk gør.

Profilering

Til profilering sætter vi belastningen til 1000 anmodninger pr. sekund, og bruger derefter pprof at fange data for at finde ud af, hvor applikationen bruger CPU-tid. Dette kan gøres ved at aktivere HTTP-endepunktet pprof, og derefter, under belastning, gem resultaterne ved hjælp af curl:

$ curl http://localhost:8080/debug/pprof/profile?seconds=29 > cpu.1000_reqs_sec_no_optimizations.prof

Resultaterne kan vises sådan her:

$ go tool pprof -http=:12345 cpu.1000_reqs_sec_no_optimizations.prof

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Grafen viser hvor og hvor meget applikationen bruger CPU-tid. Fra beskrivelsen fra Brendan Gregg:

X-aksen er stakprofilpopulationen, sorteret alfabetisk (dette er ikke tid), Y-aksen viser dybden af ​​stakken, tællet fra nul ved [øverst]. Hvert rektangel er en stakramme. Jo bredere rammen er, jo oftere er den til stede i stakkene. Det, der er øverst, kører på CPU'en, og det, der er nedenfor, er de underordnede elementer. Farverne betyder normalt ikke noget, men er simpelthen valgt tilfældigt for at adskille rammer.

Analyse - hypotese

Til tuning vil vi fokusere på at forsøge at finde spildte CPU-tid. Vi vil lede efter de største kilder til ubrugelige udgifter og fjerne dem. I betragtning af at profilering afslører meget præcist, hvor præcis applikationen bruger sin processortid, skal du muligvis gøre det flere gange, og du bliver også nødt til at ændre applikationens kildekode, køre testene igen og se, at ydeevnen nærmer sig målet.

Efter Brendan Greggs anbefalinger vil vi læse diagrammet fra top til bund. Hver linje viser en stakramme (funktionskald). Den første linje er indgangspunktet til programmet, forælderen til alle andre opkald (med andre ord, alle andre opkald vil have det på deres stak). Den næste linje er allerede anderledes:

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Hvis du holder markøren over navnet på en funktion på grafen, vil den samlede tid, den var på stakken under fejlfinding, blive vist. HTTPServe-funktionen var der 65% af tiden, andre runtime-funktioner runtime.mcall, mstart и gc, tog resten af ​​tiden. Sjov fakta: 5 % af den samlede tid bruges på DNS-forespørgsler:

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

De adresser, som programmet leder efter, tilhører Postgresql. Klik på FindByAge:

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Interessant nok viser programmet, at der i princippet er tre hovedkilder, der tilføjer forsinkelser: åbning og lukning af forbindelser, anmodning om data og forbindelse til databasen. Grafen viser, at DNS-anmodninger, åbning og lukning af forbindelser fylder omkring 13 % af den samlede eksekveringstid.

Hypotese: Genbrug af forbindelser ved hjælp af pooling bør reducere tiden for en enkelt HTTP-anmodning, hvilket tillader højere gennemløb og lavere latenstid.

Opsætning af applikationen - eksperiment

Vi opdaterer kildekoden, prøv at fjerne forbindelsen til Postgresql for hver anmodning. Den første mulighed er at bruge tilslutningspool på applikationsniveau. I dette eksperiment har vi lad os sætte det op forbindelsespooling ved hjælp af sql-driver til go:

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)

if err != nil {
   return nil, err
}

Udførelse, observation, analyse

Efter at have genstartet testen med 1000 anmodninger i sekundet, er det klart, at p99s latensniveauer er vendt tilbage til det normale med en SLO på 60ms!

Hvad koster det?

10000 anmodninger pr. sekund / 1000 anmodninger pr. server = 10 servere + 1

Lad os gøre det endnu bedre!

2000 anmodninger i sekundet

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Fordobling af belastningen viser det samme, den øverste venstre graf viser, at applikationen formår at behandle 2000 anmodninger i sekundet, p100 er lavere end 60ms, p99 opfylder SLO'en.

Med hensyn til omkostninger:

10000 anmodninger pr. sekund / 2000 anmodninger pr. server = 5 servere + 1

3000 anmodninger i sekundet

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Her kan applikationen behandle 3000 anmodninger med en p99 latency på mindre end 60ms. SLO er ikke overtrådt, og omkostningerne accepteres som følger:

10000 anmodninger pr. sekund / pr. 3000 anmodninger pr. server = 4 servere + 1 (forfatteren har rundet op, ca. oversætter)

Lad os prøve endnu en analyserunde.

Analyse - hypotese

Vi indsamler og viser resultaterne af fejlretning af applikationen ved 3000 anmodninger i sekundet:

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Stadig bruges 6 % af tiden på at etablere forbindelser. Opsætning af poolen har forbedret ydeevnen, men du kan stadig se, at applikationen fortsætter med at arbejde på at skabe nye forbindelser til databasen.

Hypotese: Forbindelser, på trods af tilstedeværelsen af ​​en pool, er stadig tabt og ryddet op, så applikationen skal nulstille dem. Indstilling af antallet af afventende forbindelser til poolstørrelsen bør hjælpe med latens ved at minimere den tid, applikationen bruger på at oprette en forbindelse.

Opsætning af applikationen - eksperiment

Forsøger at installere MaxIdleConns lig med poolstørrelsen (også beskrevet her):

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)
db.SetMaxIdleConns(8)
if err != nil {
   return nil, err
}

Udførelse, observation, analyse

3000 anmodninger i sekundet

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

p99 er mindre end 60ms med betydeligt mindre p100!

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Kontrol af flammegrafen viser, at forbindelsen ikke længere er mærkbar! Lad os tjekke mere detaljeret pg(*conn).query - vi bemærker heller ikke, at forbindelsen er etableret her.

SRE: Præstationsanalyse. Konfigurationsmetode ved hjælp af en simpel webserver i Go

Konklusion

Præstationsanalyse er afgørende for at forstå, at kundernes forventninger og ikke-funktionelle krav bliver opfyldt. Analyse ved at sammenligne observationer med kundernes forventninger kan hjælpe med at bestemme, hvad der er acceptabelt, og hvad der ikke er. Go leverer kraftfulde værktøjer indbygget i standardbiblioteket, der gør analyse enkel og tilgængelig.

Kilde: www.habr.com

Tilføj en kommentar