SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Ytelsesanalyse og justering er et kraftig verktøy for å verifisere ytelsesoverholdelse for klienter.

Ytelsesanalyse kan brukes til å se etter flaskehalser i et program ved å bruke en vitenskapelig tilnærming til å teste tuningeksperimenter. Denne artikkelen definerer en generell tilnærming til ytelsesanalyse og innstilling, med en Go-nettserver som eksempel.

Go er spesielt bra her fordi den har profileringsverktøy pprof i standardbiblioteket.

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

strategi

La oss lage en oppsummeringsliste for vår strukturelle analyse. Vi vil prøve å bruke noen data for å ta beslutninger i stedet for å gjøre endringer basert på intuisjon eller gjetting. For å gjøre dette vil vi gjøre dette:

  • Vi bestemmer optimaliseringsgrensene (krav);
  • Vi beregner transaksjonsbelastningen for systemet;
  • Vi utfører testen (oppretter data);
  • Vi observerer;
  • Vi analyserer – oppfylles alle krav?
  • Vi setter det opp vitenskapelig, lager en hypotese;
  • Vi utfører et eksperiment for å teste denne hypotesen.

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Enkel HTTP-serverarkitektur

For denne artikkelen vil vi bruke en liten HTTP-server i Golang. All kode fra denne artikkelen kan bli funnet her.

Applikasjonen som analyseres er en HTTP-server som poller Postgresql for hver forespørsel. I tillegg er det Prometheus, node_exporter og Grafana for innsamling og visning av applikasjons- og systemberegninger.

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

For å forenkle vurderer vi at for horisontal skalering (og forenkling av beregninger) distribueres hver tjeneste og database sammen:

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Definere mål

På dette trinnet bestemmer vi oss for målet. Hva prøver vi å analysere? Hvordan vet vi når det er på tide å avslutte? I denne artikkelen vil vi forestille oss at vi har kunder og at tjenesten vår vil behandle 10 000 forespørsler per sekund.

В Google SRE-bok Metoder for seleksjon og modellering diskuteres i detalj. La oss gjøre det samme og bygge modeller:

  • Forsinkelse: 99 % av forespørslene skal fullføres på mindre enn 60 ms;
  • Kostnad: Tjenesten skal forbruke det minste beløpet som vi tror er rimelig mulig. For å gjøre dette maksimerer vi gjennomstrømmingen;
  • Kapasitetsplanlegging: Krever forståelse og dokumentering av hvor mange forekomster av applikasjonen som må kjøres, inkludert generell skaleringsfunksjonalitet, og hvor mange forekomster som vil være nødvendig for å møte innledende lasting og klargjøringskrav redundans n+1.

Latency kan kreve optimalisering i tillegg til analyse, men gjennomstrømming må tydeligvis analyseres. Ved bruk av SRE SLO-prosessen kommer forsinkelsesforespørselen fra kunden eller virksomheten, representert av produktets eier. Og vår tjeneste vil oppfylle denne forpliktelsen helt fra begynnelsen uten noen innstillinger!

Sette opp et testmiljø

Ved hjelp av et testmiljø vil vi kunne legge en målt belastning på systemet vårt. For analyse vil data om ytelsen til nettjenesten bli generert.

Transaksjonsbelastning

Dette miljøet bruker vegetate for å opprette en egendefinert HTTP-forespørselsfrekvens til 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

se

Transaksjonsbelastning vil bli brukt under kjøring. I tillegg til applikasjons- (antall forespørsler, responsforsinkelse) og operativsystem (minne, CPU, IOPS)-beregninger, vil applikasjonsprofilering bli kjørt for å forstå hvor det har problemer og hvordan CPU-tiden forbrukes.

Profilering

Profilering er en type måling som lar deg se hvor CPU-tiden går når en applikasjon kjører. Den lar deg bestemme nøyaktig hvor og hvor mye prosessortid som brukes:

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Disse dataene kan brukes under analyse for å få innsikt i bortkastet CPU-tid og unødvendig arbeid som utføres. Go (pprof) kan generere profiler og visualisere dem som flammegrafer ved hjelp av et standardsett med verktøy. Jeg vil snakke om deres bruk og oppsettsveiledning senere i artikkelen.

Utførelse, observasjon, analyse.

La oss gjøre et eksperiment. Vi skal utføre, observere og analysere til vi er fornøyd med prestasjonen. La oss velge en vilkårlig lav belastningsverdi for å bruke den for å få resultatene av de første observasjonene. Ved hvert påfølgende trinn vil vi øke belastningen med en viss skaleringsfaktor, valgt med en viss variasjon. Hver belastningstestkjøring utføres med antall forespørsler justert: make load-test LOAD_TEST_RATE=X.

50 forespørsler per sekund

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Vær oppmerksom på de to øverste grafene. Øverst til venstre viser at søknaden vår behandler 50 forespørsler per sekund (tror det) og øverst til høyre viser varigheten av hver forespørsel. Begge parametrene hjelper oss å se og analysere om vi er innenfor ytelsesgrensene våre eller ikke. Rød linje på grafen HTTP-forespørselsforsinkelse viser SLO på 60ms. Linjen viser at vi ligger godt under vår maksimale responstid.

La oss se på kostnadssiden:

10000 50 forespørsler per sekund / 200 forespørsler per server = 1 servere + XNUMX

Vi kan fortsatt forbedre dette tallet.

500 forespørsler per sekund

Mer interessante ting begynner å skje når belastningen når 500 forespørsler per sekund:

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Igjen, i grafen øverst til venstre kan du se at applikasjonen registrerer normal belastning. Hvis dette ikke er tilfelle, er det et problem på serveren som programmet kjører på. Svarforsinkelsesgrafen er plassert øverst til høyre, og viser at 500 forespørsler per sekund resulterte i en svarforsinkelse på 25-40 ms. 99. persentilen passer fortsatt fint inn i 60ms SLO valgt ovenfor.

Når det gjelder kostnad:

10000 500 forespørsler per sekund / 20 forespørsler per server = 1 servere + XNUMX

Alt kan fortsatt forbedres.

1000 forespørsler per sekund

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Flott lansering! Applikasjonen viser at den behandlet 1000 forespørsler per sekund, men latensgrensen ble brutt av SLO. Dette kan sees på linje p99 i grafen øverst til høyre. Til tross for at p100-linjen er mye høyere, er de faktiske forsinkelsene høyere enn maksimum på 60ms. La oss dykke ned i profilering for å finne ut hva applikasjonen faktisk gjør.

Profilering

For profilering setter vi belastningen til 1000 forespørsler per sekund, og bruker deretter pprof for å fange data for å finne ut hvor applikasjonen bruker CPU-tid. Dette kan gjøres ved å aktivere HTTP-endepunktet pprof, og deretter, under belastning, lagre resultatene ved å bruke curl:

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

Resultatene kan vises slik:

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

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Grafen viser hvor og hvor mye applikasjonen bruker CPU-tid. Fra beskrivelsen fra Brendan Gregg:

X-aksen er stabelprofilpopulasjonen, sortert alfabetisk (dette er ikke tid), Y-aksen viser dybden på stabelen, tellende fra null på [øverst]. Hvert rektangel er en stabelramme. Jo bredere rammen er, jo oftere er den til stede i stablene. Det som er på toppen kjører på CPU, og det som er under er de underordnede elementene. Fargene betyr vanligvis ingenting, men er ganske enkelt valgt tilfeldig for å skille rammer.

Analyse - hypotese

For tuning vil vi fokusere på å prøve å finne bortkastet CPU-tid. Vi vil se etter de største kildene til ubrukelige utgifter og fjerne dem. Vel, gitt at profilering avslører veldig nøyaktig hvor nøyaktig applikasjonen bruker prosessortiden sin, kan det hende du må gjøre det flere ganger, og du må også endre applikasjonens kildekode, kjøre testene på nytt og se at ytelsen nærmer seg målet.

Etter Brendan Greggs anbefalinger vil vi lese diagrammet fra topp til bunn. Hver linje viser en stabelramme (funksjonskall). Den første linjen er inngangspunktet til programmet, overordnet for alle andre samtaler (med andre ord, alle andre samtaler vil ha det på stabelen). Den neste linjen er allerede annerledes:

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Hvis du holder markøren over navnet på en funksjon på grafen, vil den totale tiden den var på stabelen under feilsøkingen vises. HTTPServe-funksjonen var der 65 % av tiden, andre kjøretidsfunksjoner runtime.mcall, mstart и gc, tok opp resten av tiden. Moro fakta: 5 % av den totale tiden brukes på DNS-spørringer:

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Adressene som programmet ser etter tilhører Postgresql. Klikk på FindByAge:

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Interessant nok viser programmet at det i prinsippet er tre hovedkilder som legger til forsinkelser: åpning og lukking av tilkoblinger, forespørsel om data og tilkobling til databasen. Grafen viser at DNS-forespørsler, åpning og lukking av tilkoblinger tar opp omtrent 13 % av den totale utførelsestiden.

Hypotese: Gjenbruk av tilkoblinger ved bruk av pooling bør redusere tiden for en enkelt HTTP-forespørsel, og tillate høyere gjennomstrømning og lavere ventetid.

Sette opp applikasjonen - eksperiment

Vi oppdaterer kildekoden, prøv å fjerne tilkoblingen til Postgresql for hver forespørsel. Det første alternativet er å bruke tilkoblingsbasseng på søknadsnivå. I dette eksperimentet har vi la oss sette det opp tilkoblingspooling ved hjelp av sql-driver for go:

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

if err != nil {
   return nil, err
}

Utførelse, observasjon, analyse

Etter å ha startet testen på nytt med 1000 forespørsler per sekund, er det klart at p99s latensnivåer har returnert til det normale med en SLO på 60ms!

Hva koster det?

10000 1000 forespørsler per sekund / 10 forespørsler per server = 1 servere + XNUMX

La oss gjøre det enda bedre!

2000 forespørsler per sekund

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Dobling av belastningen viser det samme, grafen øverst til venstre viser at applikasjonen klarer å behandle 2000 forespørsler per sekund, p100 er lavere enn 60ms, p99 tilfredsstiller SLO.

Når det gjelder kostnad:

10000 2000 forespørsler per sekund / 5 forespørsler per server = 1 servere + XNUMX

3000 forespørsler per sekund

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Her kan applikasjonen behandle 3000 forespørsler med en p99-forsinkelse på mindre enn 60ms. SLO brytes ikke, og kostnaden aksepteres som følger:

10000 3000 forespørsler per sekund / per 4 forespørsler per server = 1 servere + XNUMX (forfatteren har rundet opp, ca. oversetter)

La oss prøve en ny analyserunde.

Analyse - hypotese

Vi samler inn og viser resultatene av feilsøking av applikasjonen med 3000 forespørsler per sekund:

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Fortsatt går 6 % av tiden med til å etablere forbindelser. Å sette opp bassenget har forbedret ytelsen, men du kan fortsatt se at applikasjonen fortsetter å jobbe med å opprette nye koblinger til databasen.

Hypotese: Tilkoblinger, til tross for tilstedeværelsen av et basseng, blir fortsatt droppet og ryddet opp, så applikasjonen må tilbakestille dem. Å angi antall ventende tilkoblinger til bassengstørrelsen bør hjelpe med ventetiden ved å minimere tiden applikasjonen bruker på å opprette en tilkobling.

Sette opp applikasjonen - eksperiment

Prøver å installere MaxIdleConns lik bassengstørrelsen (også beskrevet her):

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

Utførelse, observasjon, analyse

3000 forespørsler per sekund

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

p99 er mindre enn 60ms med betydelig mindre p100!

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Kontroll av flammegrafen viser at forbindelsen ikke lenger er merkbar! La oss sjekke mer detaljert pg(*conn).query — Vi merker heller ikke at forbindelsen etableres her.

SRE: Ytelsesanalyse. Konfigurasjonsmetode ved hjelp av en enkel webserver i Go

Konklusjon

Ytelsesanalyse er avgjørende for å forstå at kundenes forventninger og ikke-funksjonelle krav blir oppfylt. Analyse ved å sammenligne observasjoner med kundenes forventninger kan bidra til å bestemme hva som er akseptabelt og hva som ikke er det. Go gir kraftige verktøy innebygd i standardbiblioteket som gjør analyse enkel og tilgjengelig.

Kilde: www.habr.com

Legg til en kommentar