SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Prestandaanalys och justering är ett kraftfullt verktyg för att verifiera prestandaefterlevnad för kunder.

Prestandaanalys kan användas för att kontrollera om det finns flaskhalsar i ett program genom att tillämpa en vetenskaplig metod för att testa trimningsexperiment. Den här artikeln definierar ett allmänt tillvägagångssätt för prestandaanalys och justering, med hjälp av en Go-webbserver som exempel.

Go är särskilt bra här eftersom den har profileringsverktyg pprof i standardbiblioteket.

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

strategi

Låt oss skapa en sammanfattande lista för vår strukturella analys. Vi kommer att försöka använda vissa data för att fatta beslut istället för att göra ändringar baserat på intuition eller gissningar. För att göra detta kommer vi att göra så här:

  • Vi bestämmer optimeringsgränserna (krav);
  • Vi beräknar transaktionsbelastningen för systemet;
  • Vi utför testet (skapar data);
  • Vi observerar;
  • Vi analyserar – är alla krav uppfyllda?
  • Vi sätter upp det vetenskapligt, gör en hypotes;
  • Vi utför ett experiment för att testa denna hypotes.

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Enkel HTTP-serverarkitektur

För den här artikeln kommer vi att använda en liten HTTP-server i Golang. All kod från denna artikel kan hittas här.

Applikationen som analyseras är en HTTP-server som pollar Postgresql för varje begäran. Dessutom finns det Prometheus, node_exporter och Grafana för att samla in och visa applikations- och systemmått.

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

För att förenkla anser vi att för horisontell skalning (och förenkla beräkningar) distribueras varje tjänst och databas tillsammans:

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Definiera mål

I det här steget bestämmer vi oss för målet. Vad försöker vi analysera? Hur vet vi när det är dags att ta slut? I den här artikeln kommer vi att föreställa oss att vi har kunder och att vår tjänst kommer att behandla 10 000 förfrågningar per sekund.

В Google SRE-bok Metoder för urval och modellering diskuteras i detalj. Låt oss göra detsamma och bygga modeller:

  • Latens: 99 % av förfrågningarna bör slutföras på mindre än 60 ms;
  • Kostnad: Tjänsten ska förbruka det minsta belopp som vi tror är rimligt möjligt. För att göra detta maximerar vi genomströmningen;
  • Kapacitetsplanering: Kräver förståelse och dokumentation av hur många instanser av applikationen som kommer att behöva köras, inklusive övergripande skalningsfunktioner, och hur många instanser som kommer att behövas för att möta initiala laddnings- och provisioneringskrav redundans n+1.

Latens kan kräva optimering utöver analys, men genomströmningen måste helt klart analyseras. När du använder SRE SLO-processen kommer fördröjningsförfrågan från kunden eller företaget, representerad av produktägaren. Och vår tjänst kommer att uppfylla denna skyldighet från första början utan några inställningar!

Att sätta upp en testmiljö

Med hjälp av en testmiljö kommer vi att kunna lägga en uppmätt belastning på vårt system. För analys kommer data om webbtjänstens prestanda att genereras.

Transaktionsbelastning

Denna miljö använder vegetate för att skapa en anpassad HTTP-förfrågningshastighet tills den stoppas:

$ 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

titta

Transaktionsbelastning kommer att tillämpas vid körning. Förutom applikations- (antal förfrågningar, svarslatens) och operativsystem (minne, CPU, IOPS) mätvärden, kommer applikationsprofilering att köras för att förstå var det har problem och hur CPU-tid förbrukas.

Profilering

Profilering är en typ av mätning som låter dig se vart CPU-tiden tar vägen när en applikation körs. Det låter dig bestämma exakt var och hur mycket processortid som spenderas:

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Dessa data kan användas under analys för att få insikt i slöseri med CPU-tid och onödigt arbete som utförs. Go (pprof) kan generera profiler och visualisera dem som flamgrafer med hjälp av en standarduppsättning verktyg. Jag kommer att prata om deras användning och installationsguide senare i artikeln.

Utförande, observation, analys.

Låt oss göra ett experiment. Vi kommer att utföra, observera och analysera tills vi är nöjda med prestationen. Låt oss välja ett godtyckligt lågt belastningsvärde för att tillämpa det för att få resultaten av de första observationerna. Vid varje efterföljande steg kommer vi att öka belastningen med en viss skalningsfaktor, vald med viss variation. Varje belastningstestning utförs med antalet förfrågningar justerat: make load-test LOAD_TEST_RATE=X.

50 förfrågningar per sekund

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Var uppmärksam på de två översta graferna. Uppe till vänster visar att vår ansökan behandlar 50 förfrågningar per sekund (tror det) och uppe till höger visar varaktigheten av varje förfrågan. Båda parametrarna hjälper oss att se och analysera om vi är inom våra prestationsgränser eller inte. Röd linje på grafen HTTP-förfrågan fördröjning visar SLO på 60ms. Linjen visar att vi ligger långt under vår maximala svarstid.

Låt oss titta på kostnadssidan:

10000 50 förfrågningar per sekund / 200 förfrågningar per server = 1 servrar + XNUMX

Vi kan fortfarande förbättra denna siffra.

500 förfrågningar per sekund

Mer intressanta saker börjar hända när belastningen når 500 förfrågningar per sekund:

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Återigen, i det övre vänstra diagrammet kan du se att applikationen registrerar normal belastning. Om så inte är fallet finns det ett problem på servern som programmet körs på. Svarslatensdiagrammet finns längst upp till höger och visar att 500 förfrågningar per sekund resulterade i en svarsfördröjning på 25-40ms. Den 99:e percentilen passar fortfarande bra in i 60ms SLO som valts ovan.

När det gäller kostnad:

10000 500 förfrågningar per sekund / 20 förfrågningar per server = 1 servrar + XNUMX

Allt kan fortfarande förbättras.

1000 förfrågningar per sekund

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Bra lansering! Applikationen visar att den behandlade 1000 förfrågningar per sekund, men latensgränsen överträddes av SLO. Detta kan ses på rad p99 i den övre högra grafen. Trots att p100-linjen är mycket högre är de faktiska fördröjningarna högre än maxvärdet på 60ms. Låt oss dyka in i profilering för att ta reda på vad applikationen faktiskt gör.

Profilering

För profilering ställer vi in ​​belastningen till 1000 förfrågningar per sekund, och använder sedan pprof för att fånga data för att ta reda på var applikationen spenderar CPU-tid. Detta kan göras genom att aktivera HTTP-slutpunkten pprof, och sedan, under belastning, spara resultaten med curl:

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

Resultaten kan visas så här:

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

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Grafen visar var och hur mycket applikationen spenderar CPU-tid. Från beskrivningen från Brendan Gregg:

X-axeln är stapelns profilpopulation, sorterad alfabetiskt (detta är inte tid), Y-axeln visar stackens djup, räknat från noll i [överst]. Varje rektangel är en stapelram. Ju bredare ramen är, desto oftare finns den i högarna. Det som finns på toppen körs på CPU:n, och det som finns nedan är de underordnade elementen. Färgerna betyder vanligtvis ingenting, utan väljs helt enkelt slumpmässigt för att särskilja ramar.

Analys - hypotes

För justering kommer vi att fokusera på att försöka hitta bortkastad CPU-tid. Vi kommer att leta efter de största källorna till onödiga utgifter och ta bort dem. Tja, med tanke på att profilering avslöjar mycket exakt var applikationen spenderar sin processortid, kan du behöva göra det flera gånger, och du kommer också att behöva ändra applikationens källkod, köra om testerna och se att prestandan närmar sig målet.

Efter Brendan Greggs rekommendationer kommer vi att läsa diagrammet uppifrån och ned. Varje rad visar en stackram (funktionsanrop). Den första raden är ingångspunkten till programmet, föräldern till alla andra samtal (med andra ord, alla andra samtal kommer att ha det på sin stack). Nästa rad är redan annorlunda:

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Om du för markören över namnet på en funktion i grafen, kommer den totala tiden som den var i stacken under felsökningen att visas. HTTPServe-funktionen fanns där 65% av tiden, andra runtime-funktioner runtime.mcall, mstart и gc, tog upp resten av tiden. Kul fakta: 5 % av den totala tiden spenderas på DNS-frågor:

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Adresserna som programmet letar efter tillhör Postgresql. Klicka på FindByAge:

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Intressant nog visar programmet att det i princip finns tre huvudkällor som lägger till förseningar: att öppna och stänga anslutningar, begära data och ansluta till databasen. Grafen visar att DNS-förfrågningar, öppning och stängning av anslutningar tar upp cirka 13 % av den totala exekveringstiden.

Hypotes: Återanvändning av anslutningar med hjälp av pooling bör minska tiden för en enskild HTTP-förfrågan, vilket möjliggör högre genomströmning och lägre latens.

Ställa in applikationen - experiment

Vi uppdaterar källkoden, försök ta bort anslutningen till Postgresql för varje begäran. Det första alternativet är att använda anslutning pool på applikationsnivå. I detta experiment har vi låt oss ställa in det anslutningspoolning med sql-drivrutin för go:

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

if err != nil {
   return nil, err
}

Utförande, observation, analys

Efter att ha startat om testet med 1000 förfrågningar per sekund är det tydligt att p99:s latensnivåer har återgått till det normala med en SLO på 60ms!

Vad kostar det?

10000 1000 förfrågningar per sekund / 10 förfrågningar per server = 1 servrar + XNUMX

Låt oss göra det ännu bättre!

2000 förfrågningar per sekund

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Fördubbling av belastningen visar samma sak, den övre vänstra grafen visar att applikationen klarar av att behandla 2000 förfrågningar per sekund, p100 är lägre än 60ms, p99 uppfyller SLO.

När det gäller kostnad:

10000 2000 förfrågningar per sekund / 5 förfrågningar per server = 1 servrar + XNUMX

3000 förfrågningar per sekund

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Här kan applikationen behandla 3000 förfrågningar med en p99-latens på mindre än 60ms. SLO överträds inte, och kostnaden accepteras enligt följande:

10000 3000 förfrågningar per sekund / per 4 1 förfrågningar per server = XNUMX servrar + XNUMX (författaren har avrundat, cirka. översättare)

Låt oss prova en annan analysrunda.

Analys - hypotes

Vi samlar in och visar resultaten av felsökning av applikationen med 3000 förfrågningar per sekund:

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Fortfarande läggs 6% av tiden på att upprätta förbindelser. Att sätta upp poolen har förbättrat prestanda, men du kan fortfarande se att applikationen fortsätter att arbeta med att skapa nya anslutningar till databasen.

Hypotes: Anslutningar, trots närvaron av en pool, tappas fortfarande och rensas upp, så applikationen måste återställa dem. Att ställa in antalet väntande anslutningar till poolstorleken bör hjälpa till med latensen genom att minimera tiden som applikationen lägger ner på att skapa en anslutning.

Ställa in applikationen - experiment

Försöker installera MaxIdleConns lika med poolstorleken (beskrivs även här):

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

Utförande, observation, analys

3000 förfrågningar per sekund

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

p99 är mindre än 60ms med betydligt mindre p100!

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Att kontrollera flamgrafen visar att anslutningen inte längre märks! Låt oss kolla mer i detalj pg(*conn).query — Vi märker inte heller att anslutningen upprättas här.

SRE: Prestationsanalys. Konfigurationsmetod med en enkel webbserver i Go

Slutsats

Prestandaanalys är avgörande för att förstå att kundernas förväntningar och icke-funktionella krav uppfylls. Analys genom att jämföra observationer med kundernas förväntningar kan hjälpa till att avgöra vad som är acceptabelt och vad som inte är det. Go tillhandahåller kraftfulla verktyg inbyggda i standardbiblioteket som gör analysen enkel och tillgänglig.

Källa: will.com

Lägg en kommentar