SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Prestandaanalys och optimering — ett kraftfullt verktyg för att kontrollera prestandamatchning för kunder.

Prestandaanalys kan anvÀndas för att identifiera flaskhalsar i programmet, med en vetenskaplig ansats vid utvÀrdering av konfigurationsexperiment. Denna artikel definierar en generell metod för prestandaanalys och optimering med en webbserver skriven i Go som exempel.

Go Àr sÀrskilt vÀl lÀmpad för detta eftersom den erbjuder profilering verktyg. pprof i standardbiblioteket.

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Strategi

LÄt oss skapa en sammanstÀllning för vÄr strukturella analys. Vi kommer att försöka anvÀnda viss data för beslutsfattande istÀllet för att göra Àndringar baserat pÄ intuition eller gissningar. För detta gör vi sÄ hÀr:

  • Definiera optimeringsgrĂ€nser (krav);
  • BerĂ€kna transaktionsbelastningen för systemet;
  • Genomför test (skapa data);
  • Observera;
  • Analysera — uppfylls alla krav?
  • Optimera vetenskapligt, formulera en hypotes;
  • Genomför experiment för att verifiera denna hypotes.

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Arkitektur för en enkel HTTP-server

För denna artikel kommer vi att anvÀnda en liten HTTP-server pÄ Golang. All kod frÄn denna artikel kan hittas hÀr.

Det analyserade programmet Àr en HTTP-server som frÄgar Postgresql vid varje begÀran. Dessutom finns Prometheus, node_exporter och Grafana för insamling och visning av applikations- och systemmetrik.

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

För enkelhetens skull antar vi att för horisontell skalning (och förenkling av berÀkningar) varje tjÀnst och databas rullas ut tillsammans:

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Definiera mÄl

I det hÀr steget definierar vi oss sjÀlva med mÄlet. Vad försöker vi analysera? Hur vet vi nÀr det Àr dags att avsluta? I denna artikel förestÀller vi oss att vi har kunder och att vÄr tjÀnst ska hantera 10 000 begÀrningar per sekund.

I Google SRE Book diskuterar detaljerat val och modellering. Vi följer samma tillvÀgagÄngssÀtt, bygger modeller:

  • Fördröjning: 99% av begĂ€rningarna ska slutföras pĂ„ mindre Ă€n 60 ms;
  • Kostnad: tjĂ€nsten ska förbruka den minsta summa pengar som vi anser rimligt möjlig. För detta maximerar vi genomströmningen;
  • Kapacitetsplanering: det krĂ€vs en förstĂ„else och dokumentation av hur mĂ„nga instanser av applikationen som behöver startas, inklusive den övergripande skalningsfunktionen, samt hur mĂ„nga instanser som krĂ€vs för att möta de initiala belastningskraven och sĂ€kerstĂ€lla överflöd n+1.

Fördröjningen kan krÀva optimering utöver analysen, men bandbredden mÄste uppenbarligen analyseras. Vid anvÀndning av SRE-processen kommer kravet pÄ fördröjning frÄn kunden och/eller affÀren, representerade av produktÀgaren. Och vÄr tjÀnst kommer att uppfylla detta Ätagande frÄn början utan nÄgra justeringar!

Konfigurera testmiljö

Med hjÀlp av testmiljön kan vi generera en kontrollerad belastning pÄ vÄrt system. Det kommer att skapas prestandadata om webbtjÀnsten för analys.

Transaktionell belastning

Denna miljö anvÀnder Vegeta för att skapa anpassningsbar HTTP-förfrÄgningsfrekvens tills det 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

Övervakning

Under körningen kommer en transaktionell belastning att tillÀmpas. Utöver applikationsmetrik (antal förfrÄgningar, svarsfördröjningar) och operativsystemmetrik (minne, CPU, IOPS) kommer applikationsprofilering att genomföras för att förstÄ var det finns problem samt hur processor-tid konsumeras.

Profilering

Profilering Àr en typ av mÀtning som lÄter oss se vart processor-tid gÄr nÀr applikationen körs. Det gör det möjligt att avgöra exakt var och hur mycket processor-tid som anvÀnds:

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Data kan anvÀndas under analysen för att fÄ insikt om onödigt förlorad processor-tid och onödigt utförd arbete. Go (pprof) kan generera profiler och visualisera dem i form av flame graph, med hjÀlp av standarduppsÀttningen av verktyg. Jag kommer att berÀtta om deras anvÀndning och konfigurationsguide lite lÀngre ner i artikeln.

Utförande, övervakning, analys.

Vi genomför ett experiment. Vi kommer att utföra, observera och analysera tills vi Àr nöjda med prestandan. Vi vÀljer en godtyckligt lÄg belastning för att tillÀmpa den för att fÄ resultat frÄn de första observationerna. Vid varje efterföljande steg kommer vi att öka belastningen med en viss skalningsfaktor, vald med en viss spridning. Varje körning av belastningstestningen utförs med justering av antalet förfrÄgningar: make load-test LOAD_TEST_RATE=X.

50 förfrÄgningar per sekund

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Observera de tvÄ övre graferna. Den övre vÀnstra visar att vÄr applikation hanterar 50 förfrÄgningar per sekund (enligt vad den tror), och den övre högra visar varaktigheten för varje förfrÄgan. BÄda parametrarna hjÀlper oss att se och analysera: gÄr vi inom vÄra prestandagrÀnser eller inte. Den röda linjen i grafen HTTP Request Latency visar SLO pÄ 60 ms. Enligt linjen ligger vi lÄngt under vÄr maximala svarstid.

LÄt oss titta pÄ kostnadssidan:

10000 förfrÄgningar per sekund / pÄ 50 förfrÄgningar per server = 200 servrar + 1

Vi kan fortfarande förbÀttra detta vÀrde.

500 förfrÄgningar per sekund

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

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Återigen i den vĂ€nstra övre grafen ser vi att applikationen registrerar en normal belastning. Om inte, finns det ett problem pĂ„ servern dĂ€r applikationen Ă€r igĂ„ng. Grafen med svarsfördröjning finns högst upp till höger och visar att 500 förfrĂ„gningar per sekund ledde till svarsfördröjningar pĂ„ 25-40 ms. 99:e percentilen faller fortfarande inom SLO pĂ„ 60 ms, som valdes ovan.

FrÄn kostnadssynpunkt:

10000 förfrÄgningar per sekund / pÄ 500 förfrÄgningar per server = 20 servrar + 1

Det finns fortfarande utrymme för förbÀttring.

1000 förfrÄgningar per sekund

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

En fantastisk körning! Applikationen visar att den har hanterat 1000 förfrÄgningar per sekund, men grÀnsen för fördröjning har överskridits enligt SLO. Detta kan ses pÄ p99-linjen i den övre högra grafen. Trots att p100-linjen Àr mycket högre, Àr de faktiska fördröjningarna över maximivÀrdet pÄ 60 ms. LÄt oss dyka ner i profileringen för att ta reda pÄ vad applikationen faktiskt gör.

Profilering

För profilering sÀtter vi belastningen pÄ 1000 förfrÄgningar per sekund, och sedan anvÀnder vi pprof för att samla in data för att ta reda pÄ var applikationen spenderar processor tid. Detta kan göras genom att aktivera HTTP-endpointen. pprof, efter vilket du kan spara resultaten under belastning 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: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Grafen visar var och hur mycket CPU-tid applikationen anvÀnder. Enligt beskrivningen av Brendan Gregg:

X-axeln representerar stapelfyllnaden, sorterad alfabetiskt (detta Àr inte tid), Y-axeln visar stapeldjupet, rÀknat frÄn noll i [top]. Varje rektangel Àr en stapelram. Ju bredare ramen Àr, desto oftare förekommer den i stacker. Det som Àr högst upp arbetar pÄ CPU, medan det lÀngre ner Àr underordnade element. FÀrgerna har oftast ingen betydelse utan vÀljs slumpmÀssigt för att sÀrskilja ramar.

Analys - hypotes

För instÀllningen kommer vi att fokusera pÄ att försöka hitta meningslösa CPU-ÄtgÄngar. Vi ska leta efter de största kÀllorna till meningslös ÄtgÄng och ta bort dem. Med tanke pÄ att profileringen tydligt visar var applikationen anvÀnder sin CPU-tid kan det hÀnda att detta behöver göras flera gÄnger, och det kan krÀvas att Àndra applikationens kÀllkod, starta om tester och observera att prestandan nÀrmar sig planen.

Följande Brendan Greggs rekommendationer kommer vi att lÀsa grafen uppifrÄn och ner. Varje rad visar en stapelram (funktionsanrop). Första raden Àr intrÀdespunkten i programmet, förÀlder till alla andra anrop (med andra ord kommer alla andra anrop att ha den i sin stack). NÀsta rad Àr redan annorlunda:

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Om du för musen över funktionsnamnet i grafen visas den totala tiden som den var i stacken under felsökningen. Funktionen HTTPServe spenderade dÀr 65% av tiden, andra funktioner runtime.mcall, mstart och gc, tog upp resterande tid. En intressant detalj: 5% av den totala tiden spenderades pÄ DNS-förfrÄgningar:

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Adresserna som programmet söker tillhör Postgresql. Vi klickar pÄ FindByAge:

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Intressant nog visar programmet att det finns tre huvudsakliga kÀllor som bidrar till latensen: öppning/stÀngning av anslutningar, datainhÀmtning och anslutning till databasen. Grafen visar att DNS-förfrÄgningar, öppning och stÀngning av anslutningar tar cirka 13% av den totala körningstiden.

Hypotes: ÅteranvĂ€ndning av anslutningar med hjĂ€lp av en pool bör minska tiden för en enskild HTTP-förfrĂ„gan, vilket möjliggör högre kapacitet och lĂ€gre latens..

AppinstĂ€llning — experiment.

Vi uppdaterar kĂ€llkoden, testar att ta bort anslutningen till Postgresql för varje begĂ€ran. Första alternativet — anvĂ€ndning av anslutningspool. pĂ„ applikationsnivĂ„. I detta experiment kommer vi att konfigurera anslutningspoolen med hjĂ€lp av sql-drivrutinen för Go:

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

if err != nil {
   return nil, err
}

Utförande, övervakning, analys.

Efter omstart av testet med 1000 förfrÄgningar per sekund Àr det tydligt att p99 av latenserna har kommit i norm med SLO 60 ms!

Vad kostar det?

10000 förfrÄgningar per sekund / pÄ 1000 förfrÄgningar per server = 10 servrar + 1.

LÄt oss göra det Ànnu bÀttre!

2000 förfrÄgningar per sekund.

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

En dubblering av belastningen visar samma sak, den övre vÀnstra grafen visar att applikationen lyckas hantera 2000 förfrÄgningar per sekund, p100 Àr under 60 ms, p99 uppfyller SLO.

FrÄn kostnadssynpunkt:

10000 förfrÄgningar per sekund / pÄ 2000 förfrÄgningar per server = 5 servrar + 1.

3000 förfrÄgningar per sekund.

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

HÀr kan applikationen hantera 3000 förfrÄgningar med en p99-latens under 60 ms. SLO bryts inte, och kostnaden tas som:

10000 förfrÄgningar per sekund / pÄ 3000 förfrÄgningar per server = 4 servrar + 1. (författaren avrundade uppÄt, översÀttarens anm.)

LÄt oss göra ett ytterligare analysomgÄng.

Analys - hypotes

Vi samlar in och visar resultaten frÄn avkodningen av applikationen pÄ 3000 förfrÄgningar per sekund:

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Ännu 6% av tiden spenderas pĂ„ att etablera anslutningar. InstĂ€llningen av poolen har förbĂ€ttrat prestandan, men det Ă€r fortfarande tydligt att applikationen fortsĂ€tter att skapa nya anslutningar till databasen.

Hypotes: Anslutningar, trots existensen av en pool, avvisas och stÀdas fortfarande, sÄ applikationen mÄste Äteretablera dem. InstÀllningen av antalet vÀntande anslutningar till poolens storlek bör hjÀlpa till med latensen genom att minimera tiden som applikationen spenderar pÄ att skapa en anslutning..

AppinstĂ€llning — experiment.

Vi försöker stÀlla in MaxIdleConns lika med poolens storlek (som Àven beskrivs i hÀr):

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

Utförande, övervakning, analys.

3000 förfrÄgningar per sekund.

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

p99 under 60 ms med betydligt lÀgre p100!

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Kontrollen av flame graph visar att anslutningen inte lĂ€ngre Ă€r mĂ€rkbar! LĂ„t oss undersöka nĂ€rmare. pg(*conn).query — vi mĂ€rker inte att anslutningen etableras hĂ€r heller.

SRE: Prestandaanalys. Konfigurationsmetod med en enkel webbserver pÄ Go

Slutsats

Prestandaanalys Àr avgörande för att förstÄ om kundernas förvÀntningar och icke-funktionella krav uppfylls. Genom att jÀmföra observationer med kundernas förvÀntningar kan man avgöra vad som Àr acceptabelt och vad som inte Àr det. Go erbjuder effektiva verktyg, inbyggda i standardbiblioteket, som gör analysen enkel och tillgÀnglig.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster