Jõudluse analüüs ja seadistamine — tugev tööriist jõudlusnõuete vastavuse kontrollimiseks klientidele.
Jõudluse analüüsi saab kasutada programmide kitsaskohtade kontrollimiseks, rakendades teaduslikku lähenemist seadistuste eksperimentide testimisel. See artikkel määratleb üldise lähenemise jõudluse analüüsile ja seadistusele, kasutades näitena Go veebiserverit.
Go sobib siinkohal eriti hästi, kuna sellel on profiilingu tööriistad pprof standardses teegis.

Strateegia
Loome meie struktuuri analüüsi jaoks kokkuvõtliku nimekirja. Püüame kasutada andmeid otsuste tegemiseks, mitte teha muudatusi, mis põhinevad intuitsioonil või arvamisel. Sellega seoses teeme järgmist:
- Määrame optimeerimise piirid (nõuded);
- Arvutame süsteemi tehingukoormuse;
- Teeme testi (loome andmed);
- Jälgime;
- Analüüsime — kas kõik nõuded on täidetud?
- Seadistame teaduslikult, teeme hüpoteesi;
- Teeme eksperimenti selle hüpoteesi kontrollimiseks.

Lihtsa HTTP serveri arhitektuur
Selles artiklis kasutame väikest HTTP-serverit Golangi peal. Kogu selle artikli kood on saadaval. .
Analüüsitav rakendus on HTTP-server, mis küsib iga küsimuse puhul Postgresql'lt. Samuti on olemas Prometheus, node_exporter ja Grafana, et koguda ja kuvada rakenduse ja süsteemi mõõdikuid.

Lihtsustamiseks oletame, et iga teenus ja andmebaas paigaldatakse koos horisontaalse skaleerimise (ja arvutuste lihtsustamise) jaoks:

Määratleme eesmärgid
Selles etapis määratleme eesmärgi. Mida me püüame analüüsida? Kuidas me saame teada, millal lõpetada? Sellest artiklist lähtuvalt oletame, et meil on kliendid ja et meie teenus hakkab töötlema 10 000 päringut sekundis.
V käsitletakse üksikasjalikult valikute ja modelleerimise viise. Teeme samuti, ehitame mudelid:
- Viivitus: 99% päringutest peaks toimuma vähem kui 60 ms;
- Hind: teenus peaks tarbima minimaalset summat raha, mida peame mõistlikuks. Selleks maksimeerime läbilaskevõime.
- Mahutite planeerimine: vajalik on mõista ja dokumenteerida, kui palju rakenduse koopiaid on vaja käivitada, sealhulgas üldine skaleerimise funktsioon, samuti kui palju koopiaid on vajalik algsete koormuse nõuete rahuldamiseks ja tagamise. .
Latentsus võib vajada optimeerimist koos analüüsiga, kuid läbilaskevõime tuleb selgelt analüüsida. SRE protsessi SLO kasutamisel tuleneb latentsuse nõue kliendilt või ettevõttelt, mille esindab toote omanik. Meie teenus täidab selle kohustuse algusest peale ilma igasuguste seadistusteta!
Konfigureeritav testkeskkond
Testkeskkonna abil suudame meie süsteemile doseeritud koormust anda. Analüüsi jaoks luuakse veebiteenuse jõudluse andmed.
Tehingukoormus
See keskkond kasutab kohandatud HTTP päringute sageduse genereerimiseks, kuni see peatatakse:
$ 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 reportJälgimine
Teostamise käigus rakendatakse tehingukoormust. Lisaks rakenduse mõõdikutele (päringute arv, vastuse viivitus) ja operatsioonisüsteemi (mälumahu, CPU, IOPS) käivitatakse rakenduse profiilimine, et mõista, kus selles on probleeme, ning kuidas protsessorite mõiste kasutamine toimub.
Profiilimine
Profiilimine on mõõtmise tüüp, mis võimaldab näha, kuhu suunatakse protsessorite aeg rakenduse töö käigus. See aitab määrata, kus ja kui palju protsessorite aega kulutatakse:

Neid andmeid saab kasutada analüüsi käigus, et saada ülevaade mõttetult kulutatud protsessorite ajast ja teostatud tarbetust tööst. Go (pprof) suudab genereerida profiile ja visualiseerida neid leekgraafikuna, kasutades standardset tööriistade komplekti. Räägin nende kasutamisest ja seadistamisjuhendist allpool artiklis.
Teostamine, jälgimine, analüüs.
Teeme eksperimenti. Me jälgime ja analüüsime, kuni jõuame saavutada soovitud jõudluse. Valime suvaliselt madala koormuse, et rakendada seda esimeste tähelepanekute saamiseks. Igal järgmisel sammul suurendame koormust mingi skaleerimisteguriga, mis on valitud teatud vahemikus. Iga koormustesti käivitatakse, reguleerides päringute arvu: make load-test LOAD_TEST_RATE=X.
50 päringut sekundis

Pange tähele kahte ülemist graafikut. Ülemine vasak näitab, et meie rakendus töötleb 50 päringut sekundis (tema arvates), ja ülemine parem graafik näitab iga päringu kestust. Mõlemad parameetrid aitavad meil vaatleda ja analüüsida: kas jääme oma jõudluspiiridesse või mitte. Grafiku punane joon HTTP Päringu Latentsus näitab SLO-d 60 ms. Joone järgi on näha, et oleme oma maksimaalsest vastuse ajast kaugel allpool.
Vaadakem kulupoolt:
10000 päringut sekundis / 50 päringut serveri kohta = 200 serverit + 1
Saame seda näitajat ikka veel parandada.
500 päringut sekundis
Huvitavamad asjad hakkavad juhtuma, kui koormus ulatub 500 päringuni sekundis:

Nagu vasakul ülemisel graafikul näha, registreerib rakendus tavalise koormuse. Kui see nii ei ole, on serveris, kus rakendus töötab, probleem. Ülemisel paremal graafikul, mis näitab vastuse viivitust, on näha, et 500 päringut sekundis viis vastuse viivituse 25–40 ms. 99. protsentiil mahtus endiselt suurepäraselt valitud SLO-sse 60 ms.
Kulude seisukohalt:
10000 päringut sekundis / 500 päringut serveri kohta = 20 serverit + 1
Seda saab endiselt parandada.
1000 päringut sekundis

Suurepärane käivitus! Rakendus näitab, et see töötles 1000 päringut sekundis, kuid viivituse piirang SLO-s ületati. Seda on näha p99 joone pealt ülemisel paremal graafikul. Kuigi p100 joon on palju kõrgem, on reaalsed viivitused üle maksimaalse 60 ms. Sukeldugem profiilimiseks, et teada saada, mida rakendus tegelikult teeb.
Profiilimine
Profiilimiseks paneme koormuse 1000 päringuni sekundis, seejärel kasutame pprof andmete kogumiseks, et mõista, kus rakendus protsessoriaega kulutab. Seda saab teha, aktiveerides HTTP lõpp-punkti pprof, pärast mida koormuse all salvestame tulemused kasutades curl:
$ curl http://localhost:8080/debug/pprof/profile?seconds=29 > cpu.1000_reqs_sec_no_optimizations.profTulemusi saab kuvada järgmiselt:
$ go tool pprof -http=:12345 cpu.1000_reqs_sec_no_optimizations.prof
Graafik näitab, kus ja kui palju rakendus protsessoriaega kulutab. Kirjelduse kohaselt :
X-telg – virna täitmine, sorteeritud tähestikulises järjekorras (see ei ole aeg), Y-telg näitab virna sügavust, alates nullist [top]-ist. Iga ristkülik on virna kaader. Mida laiem kaader, seda sagedamini see virnades esineb. Ülalpool – töötab CPU-l, allpool – alamsüsteemid. Värvid ei tähenda tavaliselt midagi, vaid valitakse lihtsalt juhuslikult, et eristada kaadreid.
Analüüs – hüpotees
Seadistamiseks keskendume katsetele leida tarbetud protsessoriaja kulutusi. Otsime suurimaid tarbetute kulutuste allikaid ja eemaldame need. Kuna profiilimine näitab väga täpselt, kus rakendus oma protsessoriaega kulutab, võib osutuda vajalikuks seda mitu korda korrata ning samuti tuleb muuta rakenduse lähtekoodi, taaskäivitada testid ja jälgida, et jõudlus oleks kavandatule lähedal.
Järgime Brendan Greggi soovitusi ja vaatame graafikut ülevalt alla. Iga rida esindab virnastamisraami (funktsiooni üleskutset). Esimene rida on programmi sisendpunkt, teiste kutsungite vanem (teiste sõnadega, kõik teised kutsungid on tema virnas). Järgmine rida on juba erinev:
![]()
Kui viidate funktsiooni nimele graafikul, kuvatakse koguaeg, kui kaua ta virnas oli silumi ajal. Funktsioon HTTPServe oli seal 65% ajast, teised runtime-funktsioonid, runtime.mcall, mstart ja gc, võtsid ülejäänud aja. Huvi asjaolu: 5% koguajast kulus DNS-päringutele:

Aadressid, mida programm otsib, kuuluvad Postgresql'ile. Klikkame FindByAge:

Huvitav on see, et programm näitab, et põhimõtteliselt on kolm peamist allikat, mis lisavad viivitusi: ühenduste avamine ja sulgemine, andmete päring ning andmebaasi sisselogimine. Graafikult on näha, et DNS-i päringud, ühenduste avamine ja sulgemine võtavad ligikaudu 13% kogu töötlusajast.
Hüpotees: Ühenduste taaskasutamine ühenduste puu kaudu peaks vähendama ühe HTTP päringu aega, võimaldades kõrgemat läbilaskevõimet ja madalamaid viivitusi..
Rakenduse seadistamine — eksperiment.
Värskendame lähtekoodi, proovime eemaldada Postgresql-iga ühenduse iga päringu korral. Esimene variant — kasutamine. Selles eksperimendis me ühenduste puu sql draiveri abil go jaoks:
db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)
if err != nil {
return nil, err
}Täideviimine, jälgimine, analüüs.
Pärast 1000 päringu sekundis testi taaskäivitamist on näha, et p99 viivitused on normi lähedale jõudnud, SLO 60ms!
Kuidas on kulud?
10000 päringut sekundis / 1000 päringut serverisse = 10 serverit + 1.
Teeme veelgi paremini!
2000 päringut sekundis.

Koormuse kahekordistamine näitab sama: vasaku ülanurga graafik illustreerib, et rakendus suudab töötada välja 2000 päringut sekundis, p100 alla 60 ms, p99 vastab SLO-le.
Kulude seisukohalt:
10000 päringut sekundis / 2000 päringut serveri kohta = 5 serverit + 1
3000 päringut sekundis

Siin suudab rakendus töödelda 3000 päringut, p99 viivitusega alla 60 ms. SLO-d ei rikutud ja kulus on arvutatud järgmiselt:
10000 päringut sekundis / 3000 päringut serveri kohta = 4 serverit + 1 (autor ümardas suuremaks, tõlkija märk.)
Proovime veel ühte analüüsi vooru.
Analüüs – hüpotees
Kogume ja kuvame tulemusi rakenduse tõrkeotsingust 3000 päringuga sekundis:

Endiselt 6% ajast kulub ühenduste loomisele. Loodud bassein parandas jõudlust, kuid on näha, et rakendus jätkab uute ühenduste loomist andmebaasiga.
Hüpotees: Ühendused, hoolimata basseinist, eemaldatakse ja puhastatakse endiselt, seetõttu peab rakendus need uuesti looma. Ootavate ühenduste arvu seadmine basseini suurusele peaks aitama viivitust vähendada, minimeerides aja, mida rakendus kulutab ühenduse loomisele..
Rakenduse seadistamine — eksperiment.
Proovime seada samade suurusele basseinile (kirjeldatakse ka ):
db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)
db.SetMaxIdleConns(8)
if err != nil {
return nil, err
}Täideviimine, jälgimine, analüüs.
3000 päringut sekundis

p99 alla 60ms koos oluliselt madalama p100-ga!

Flame graafi kontroll näitab, et ühenduse seadmine pole enam nähtav! Kontrollime lähemalt pg(*conn).query — ka siin ei märka ühenduse seadmist.

Kokkuvõte
Mugavuse analüüs on kriitilise tähtsusega, et mõista, kas kliendi ootused ja mittefunktsionaalsed nõuded on täidetud. Analüüs, võrreldes tähelepanekuid klientide ootustega, võib aidata kindlaks teha, mis on vastuvõetav ja mis mitte. Go pakub tõhusaid tööriistu, mis on integreeritud standardteeki, et muuta analüüs lihtsaks ja kergesti ligipääsetavaks.
Allikas: habr.com
