SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Анализата и подесувањето на перформансите се моќна алатка за проверка на усогласеноста со перформансите за клиентите.

Анализата на перформансите може да се користи за проверка на тесни грла во програмата со примена на научен пристап за тестирање на експерименти за подесување. Оваа статија дефинира општ пристап за анализа и подесување на перформансите, користејќи го веб-серверот Go како пример.

Go е особено добар овде бидејќи има алатки за профилирање pprof во стандардната библиотека.

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

стратегија

Ајде да создадеме збирна листа за нашата структурна анализа. Ќе се обидеме да користиме некои податоци за да донесуваме одлуки наместо да правиме промени врз основа на интуиција или претпоставки. За да го направите ова, ќе го направиме ова:

  • Ги одредуваме границите (барањата) за оптимизација;
  • Ние го пресметуваме оптоварувањето на трансакцијата за системот;
  • Го извршуваме тестот (создаваме податоци);
  • Ние набљудуваме;
  • Ние анализираме - дали се исполнети сите барања?
  • Го поставивме научно, правиме хипотеза;
  • Ние правиме експеримент за да ја тестираме оваа хипотеза.

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Едноставна архитектура на HTTP сервер

За оваа статија ќе користиме мал HTTP сервер во Голанг. Сите кодови од овој напис може да се најдат тука.

Апликацијата што се анализира е HTTP сервер кој го анкетира Postgresql за секое барање. Дополнително, тука се и Prometheus, node_exporter и Grafana за собирање и прикажување на метрика на апликациите и системот.

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

За да се поедностави, сметаме дека за хоризонтално скалирање (и поедноставување на пресметките) секоја услуга и база на податоци се распоредени заедно:

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Дефинирање на цели

На овој чекор одлучуваме за целта. Што се обидуваме да анализираме? Како да знаеме кога е време да се заврши? Во оваа статија ќе замислиме дека имаме клиенти и дека нашата услуга ќе обработува 10 барања во секунда.

В Google SRE Book Детално се дискутираат методите на селекција и моделирање. Ајде да го сториме истото и да изградиме модели:

  • Латентност: 99% од барањата треба да се завршат за помалку од 60 ms;
  • Цена: Услугата треба да троши минимален износ на пари што мислиме дека е разумно возможен. За да го направите ова, ја максимизираме пропусната моќ;
  • Планирање капацитет: Потребно е разбирање и документирање колку примероци од апликацијата ќе треба да се извршат, вклучително и целокупната функционалност на скалирање и колку примероци ќе бидат потребни за да се исполнат барањата за првично оптоварување и обезбедување вишок n+1.

Доцнењето може да бара оптимизација покрај анализата, но пропусната моќ јасно треба да се анализира. При користење на процесот SRE SLO, барањето за одложување доаѓа од клиентот или бизнисот, претставен од сопственикот на производот. И нашата услуга ќе ја исполни оваа обврска од самиот почеток без никакви поставки!

Поставување средина за тестирање

Со помош на средина за тестирање, ќе можеме да поставиме измерено оптоварување на нашиот систем. За анализа ќе се генерираат податоци за перформансите на веб сервисот.

Оптоварување на трансакцијата

Оваа средина користи Vegeta за да креирате прилагодена стапка на барања за HTTP додека не престане:

$ 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

Набудување

Трансакциското оптоварување ќе се примени при извршување. Покрај метриката на апликацијата (број на барања, латентност на одговор) и оперативниот систем (меморија, процесор, IOPS), ќе се изврши профилирање на апликацијата за да се разбере каде има проблеми и како се троши времето на процесорот.

Профилирање

Профилирањето е вид на мерење што ви овозможува да видите каде оди времето на процесорот кога работи апликацијата. Тоа ви овозможува да одредите точно каде и колку време се троши на процесорот:

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Овие податоци може да се користат за време на анализата за да се добие увид во потрошеното време на процесорот и непотребната работа што се извршува. Go (pprof) може да генерира профили и да ги визуелизира како графикони со пламен користејќи стандарден сет на алатки. Ќе зборувам за нивната употреба и водич за поставување подоцна во статијата.

Извршување, набљудување, анализа.

Ајде да направиме експеримент. Ќе настапуваме, набљудуваме и анализираме додека не бидеме задоволни од изведбата. Дозволете ни да избереме произволно мала вредност на оптоварување за да ја примениме за да ги добиеме резултатите од првите набљудувања. На секој следен чекор ќе го зголемуваме оптоварувањето со одреден фактор на скалирање, избран со одредена варијација. Секое тестирање на оптоварување се изведува со прилагоден број на барања: make load-test LOAD_TEST_RATE=X.

50 барања во секунда

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Обрнете внимание на првите два графикони. Горниот лев агол покажува дека нашата апликација обработува 50 барања во секунда (мисли) а горе десно покажува времетраењето на секое барање. Двата параметри ни помагаат да погледнеме и анализираме дали сме во границите на нашите перформанси или не. Црвена линија на графиконот Латентност на барање HTTP покажува SLO на 60ms. Линијата покажува дека сме далеку под нашето максимално време на одговор.

Да ја погледнеме страната на трошоците:

10000 барања во секунда / 50 барања по сервер = 200 сервери + 1

Сè уште можеме да ја подобриме оваа бројка.

500 барања во секунда

Поинтересните работи почнуваат да се случуваат кога оптоварувањето ќе достигне 500 барања во секунда:

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Повторно, во горниот лев графикон можете да видите дека апликацијата снима нормално оптоварување. Ако тоа не е случај, има проблем на серверот на кој работи апликацијата. Графикот на латентност на одговорот се наоѓа горе десно, покажувајќи дека 500 барања во секунда резултирале со доцнење на одговорот од 25-40 ms. 99-от перцентил сепак убаво се вклопува во 60ms SLO избрано погоре.

Во однос на трошоците:

10000 барања во секунда / 500 барања по сервер = 20 сервери + 1

Сè уште може да се подобри.

1000 барања во секунда

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Одлично лансирање! Апликацијата покажува дека обработувала 1000 барања во секунда, но границата на латентност била прекршена од SLO. Ова може да се види во линијата p99 во горниот десен график. И покрај фактот дека линијата p100 е многу повисока, вистинските доцнења се повисоки од максималните 60 ms. Ајде да се нурнеме во профилирање за да дознаеме што всушност прави апликацијата.

Профилирање

За профилирање, го поставивме оптоварувањето на 1000 барања во секунда, а потоа користиме pprof за снимање податоци за да дознаете каде апликацијата го троши времето на процесорот. Ова може да се направи со активирање на HTTP крајната точка pprof, а потоа, под оптоварување, зачувајте ги резултатите користејќи curl:

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

Резултатите може да се прикажат вака:

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

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Графиконот покажува каде и колку апликацијата троши време на процесорот. Од описот од Брендан Грег:

Оската X е популација на профилот на магацинот, подредена по азбучен ред (ова не е време), оската Y ја покажува длабочината на оџакот, броејќи од нула на [горе]. Секој правоаголник е рамка на магацинот. Колку е поширока рамката, толку почесто е присутна во оџаците. Она што е на врвот работи на процесорот, а она што е подолу се детските елементи. Боите обично не значат ништо, туку едноставно се избираат по случаен избор за да се разликуваат рамки.

Анализа - хипотеза

За подесување, ќе се фокусираме на обидот да најдеме потрошено време на процесорот. Ќе ги бараме најголемите извори на бескорисно трошење и ќе ги отстраниме. Па, со оглед на фактот дека профилирањето многу прецизно открива каде точно апликацијата го троши времето на процесорот, можеби ќе треба да го направите ова неколку пати, а исто така ќе треба да го промените изворниот код на апликацијата, да ги повторите тестовите и да видите дека перформансите се приближуваат Целта.

Следејќи ги препораките на Брендан Грег, ќе ја читаме табелата од врвот до дното. Секоја линија прикажува рамка на стек (повик на функција). Првата линија е влезната точка во програмата, родител на сите други повици (со други зборови, сите други повици ќе го имаат на нивниот куп). Следната линија е веќе различна:

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Ако го ставите курсорот над името на функцијата на графиконот, ќе се прикаже вкупното време што беше на оџакот за време на отстранувањето грешки. Функцијата HTTPServe беше таму во 65% од времето, други функции за време на траење runtime.mcall, mstart и gc, го одзеде остатокот од времето. Забавен факт: 5% од вкупното време се троши на прашања за DNS:

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Адресите што ги бара програмата припаѓаат на Postgresql. Кликнете на FindByAge:

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Интересно, програмата покажува дека, во принцип, постојат три главни извори кои додаваат одложувања: отворање и затворање врски, барање податоци и поврзување со базата на податоци. Графиконот покажува дека барањата за DNS, отворање и затворање врски заземаат околу 13% од вкупното време на извршување.

Хипотеза: Повторната употреба на врски користејќи здружување треба да го намали времето на едно барање HTTP, овозможувајќи поголема пропусност и помала латентност.

Поставување на апликацијата - експеримент

Го ажурираме изворниот код, се обидуваме да ја отстраниме врската со Postgresql за секое барање. Првата опција е да се користи поврзувачки базен на ниво на апликација. Во овој експеримент ние ајде да го поставиме здружување на конекции со помош на двигател SQL за Go:

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

if err != nil {
   return nil, err
}

Извршување, набљудување, анализа

По рестартирање на тестот со 1000 барања во секунда, јасно е дека нивоата на латентност на p99 се вратија во нормала со SLO од 60 ms!

Која е цената?

10000 барања во секунда / 1000 барања по сервер = 10 сервери + 1

Ајде да го направиме тоа уште подобро!

2000 барања во секунда

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Удвојувањето на оптоварувањето го покажува истото, горниот лев график покажува дека апликацијата успева да обработи 2000 барања во секунда, p100 е помал од 60ms, p99 го задоволува SLO.

Во однос на трошоците:

10000 барања во секунда / 2000 барања по сервер = 5 сервери + 1

3000 барања во секунда

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Овде апликацијата може да обработи 3000 барања со латентност на p99 помала од 60 ms. СЛО не е повредено, а трошокот се прифаќа на следниов начин:

10000 барања во секунда / на 3000 барања по сервер = 4 сервери + 1 (авторот заокружи, прибл. преведувач)

Ајде да пробаме уште една рунда анализа.

Анализа - хипотеза

Ги собираме и прикажуваме резултатите од дебагирањето на апликацијата со 3000 барања во секунда:

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Сè уште 6% од времето се троши на воспоставување врски. Поставувањето на базенот ги подобри перформансите, но сепак можете да видите дека апликацијата продолжува да работи на создавање нови врски со базата на податоци.

Хипотеза: Конекциите, и покрај присуството на базен, сè уште се испуштени и исчистени, така што апликацијата треба да ги ресетира. Поставувањето на бројот на нерешени врски со големината на базенот треба да помогне во латентноста со минимизирање на времето што апликацијата го поминува за создавање врска.

Поставување на апликацијата - експеримент

Се обидува да инсталира MaxIdleConns еднаква на големината на базенот (исто така опишан тука):

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

Извршување, набљудување, анализа

3000 барања во секунда

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

p99 е помалку од 60ms со значително помалку p100!

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Проверката на графиконот за пламен покажува дека врската повеќе не се забележува! Ајде да провериме подетално pg(*conn).query — Исто така, не забележуваме дека врската е воспоставена овде.

SRE: Анализа на перформанси. Метод на конфигурација користејќи едноставен веб-сервер во Go

Заклучок

Анализата на перформансите е клучна за да се разбере дека очекувањата на клиентите и нефункционалните барања се исполнети. Анализата со споредување на набљудувањата со очекувањата на клиентите може да помогне да се утврди што е прифатливо, а што не. Go обезбедува моќни алатки вградени во стандардната библиотека кои ја прават анализата едноставна и достапна.

Извор: www.habr.com

Додадете коментар