Производителност на мрежови приложения Linux. Въведение

Уеб приложенията вече се използват навсякъде, а сред всички транспортни протоколи, HTTP заема голям дял. При изследване на нюансите на разработването на уеб приложения, повечето отдадени много малко внимание на операционната система, в която тези приложения наистина работят. Разделянето на разработката (Dev) и експлоатацията (Ops) само влошава ситуацията. Но с разпространението на културата DevOps, разработчиците започват да носят отговорност за стартирането на своите приложения в облака, поради което е изключително полезно да се запознаят задълбочено с бекенда на операционната система. Това е особено полезно, ако се опитвате да внедрите система за хиляди или десетки хиляди едновременни връзки.

Ограниченията в уеб услугите са много подобни на ограниченията в другите приложения. Независимо дали става въпрос за балансировщики на натоварване или бази данни, всички тези приложения имат сходни проблеми в среда с висока производителност. Разбирането на тези основополагающи ограничения и начините за преодоляването им в крайна сметка ще ви позволи да оцените производителността и мащабируемостта на вашите уеб приложения.

Пиша тази серия статии в отговор на въпросите на млади разработчици, които искат да станат добре информирани системни архитекти. Невъзможно е да се разберат ясно методите за оптимизация на приложения под Linux, без да се задълбочим в основите на това как работят на ниво операционна система. Въпреки че съществуват много видове приложения, в този цикъл искам да изследвам мрежови приложения, а не десктоп приложения, като браузър или текстов редактор. Този материал е насочен към разработчици и архитекти, които искат да разберат как работят програмите под Linux или Unix и как да ги структурират за висока производителност.

Linux е сървърна операционна система и в повечето случаи вашите приложения работят именно на тази ОС. Въпреки че говоря "Linux", през по-голямата част от времето можете уверено да предполагате, че става въпрос за всички Unix-подобни операционни системи като цяло. Въпреки това, не съм тествал придружаващия код на други системи. Така че, ако се интересувате от FreeBSD или OpenBSD, резултатът може да се различава. Когато тествам нещо специфично за Linux, ще го посоча.

Въпреки че можете да използвате придобитите знания, за да създадете приложение от нулата и то ще бъде отлично оптимизирано, по-добре е да не правите това. Ако напишете нов уеб сървър на C или C++ за бизнес приложението на вашата организация, това може да бъде последният ви ден на работа. Обаче знанието на структурата на тези приложения ще помогне при избора на вече съществуващи програми. Ще можете да сравнявате системи, базирани на процеси, с системи, базирани на нишки, а също и на събития. Ще разберете и оцените защо Nginx работи по-добре от Apache httpd, и защо приложение на Python, базирано на Tornado, може да обслужва повече потребители в сравнение с приложение на Python, базирано на Django.

ZeroHTTPd: инструмент за обучение

ZeroHTTPd — уеб сървър, който написах от нулата на C като учебен инструмент. Няма външни зависимости, включително достъп до Redis. Ние стартираме собствени процедури Redis. Повече информация по-долу.

Въпреки че можем дълго да обсъждаме теорията, няма нищо по-добро от това да напишем код, да го стартираме и да сравним всичките сървърни архитектури помежду им. Това е най-наочният метод. Затова ще напишем прост уеб сървър ZeroHTTPd, прилагайки всяка модел: базиран на процеси, нишки и събития. Ще проверим всеки от тези сървъри и ще видим как работят помежду си. ZeroHTTPd е реализиран в един файл C. В него, базираният на събития сървър включва uthash, отлична реализация на хеш-таблица, която идва в един заглавен файл. В останалите случаи няма зависимости, за да не усложняваме проекта.

Кодът съдържа много коментари, за да помогне в разбирането. Будейки прост уеб сървър с няколко реда код, ZeroHTTPd също така представлява минимален фреймворк за уеб разработка. Има ограничена функционалност, но е в състояние да издава статични файлове и много прости "динамични" страници. Трябва да кажа, че ZeroHTTPd е подходящ за обучение как да създавате високопроизводителни Linux приложения. По принцип, повечето уеб услуги чакат заявките, проверяват ги и ги обработват. Именно това ще прави ZeroHTTPd. Това е инструмент за обучение, а не за продукция. Не е силен в обработката на грешки и едва ли ще се похвали с най-добрите практики за сигурност (о, да, използвах strcpy) или с изощрёнными трюками на языке C. Однако я надеюсь, что он успешно справится со своей задачей.

Производителност на мрежови приложения Linux. Въведение
Начальная страница ZeroHTTPd. Той предоставя различни типове файлове, включително изображения.

Приложение за гостева книга.

Съвременните уеб-приложения обикновено не се ограничават до статични файлове. Те имат сложни взаимодействия с различни бази данни, кешове и т.н. Затова ще създадем простo уеб-приложение, наречено „Гостева книга“, където посетителите могат да оставят записи с имената си. В гостевата книга се запазват предишно оставените записи. Има също и брояч на посетителите в долната част на страницата.

Производителност на мрежови приложения Linux. Въведение
Уеб-приложение „Гостева книга“ ZeroHTTPd.

Броячът на посетителите и записите на гостевата книга се съхраняват в Redis. За комуникацията с Redis са реализирани собствени процедури, които не зависят от външна библиотека. Не съм голям почитател на важния код в ситуации, когато съществуват открити и добре тествани решения. Но целта на ZeroHTTPd е да разгледа производителността на Linux и достъпа до външни служби, докато обслужването на HTTP заявки сериозно влияе на производителността. Ние трябва напълно да контролираме комуникацията с Redis във всяка от нашите сървърни архитектури. В една архитектура използваме блокиращи повиквания, а в други – процедури на базата на събития. Използването на външна клиентска библиотека Redis няма да осигури такъв контрол. Освен това, нашият малък клиент Redis изпълнява само няколко функции (извличане, настройка и увеличаване на ключа; извличане и добавяне към масив). Освен това, протоколът на Redis е изключително елегантен и прост. Не е нужно дори да се учи специално. Самият факт, че цялата работа на протокола се осъществява с около сто реда код, говори за това колко е добре замислен.

На следващата рисунка се показва как действат приложението, когато клиент (браузърът) прави запитване. /guestbookURL.

Производителност на мрежови приложения Linux. Въведение
Механизмът на работа на приложението за гостева книга.

Когато е нужно да се издаде страница на гостната книга, се прави едно извикване към файловата система за четене на шаблона в паметта и три мрежови извиквания към Redis. Файлът на шаблона съдържа голяма част от съдържанието на HTML за страницата на екрана отгоре. Там също има специални запълнители за динамичната част на съдържанието: записи и брояч на посетителите. Получаваме ги от Redis, вмъкваме ги на страницата и предоставяме на клиента напълно оформено съдържание. Третото извикване към Redis може да бъде избегнато, тъй като Redis връща нова стойност на ключа при увеличаване. Въпреки това, за нашия сървър с асинхронна архитектура на базата на събития, множеството мрежови извиквания е добро изпитание за учебни цели. Следователно, отхвърляме връщаната стойност на Redis за броя на посетителите и го запитваме с отделно извикване.

Сървърни архитектури ZeroHTTPd

Строим седем версии на ZeroHTTPd с еднаква функционалност, но с различни архитектури:

  • Итеративна
  • Форк сървър (един дъщерен процес на заявка)
  • Пре-форк сървър (предварително форкване на процеси)
  • Сървър с потоци на изпълнение (един поток на заявка)
  • Сървър с предварително създадени потоци
  • Архитектура на база poll()
  • Архитектура на база epoll

Измерваме производителността на всяка архитектура, натоварвайки сървъра с HTTP-заявки. Но при сравняване на архитектури с висока степен на паралелизъм, броят на заявките се увеличава. Тестваме три пъти и смятаме средното.

Методология на тестиране

Производителност на мрежови приложения Linux. Въведение
Настройка за натоварочно тестване на ZeroHTTPd

Важно е, че при извършване на тестовете, всички компоненти да не работят на една машина. В този случай ОС носи допълнителни разходи за планиране, тъй като компонентите се конкурират за CPU. Измерването на разходите на операционната система с всяка от избраните сървърни архитектури е една от най-важните цели на това упражнение. Добавянето на повече променливи ще бъде пагубно за процеса. Следователно, настройката на изображението по-горе работи най-добре.

Какво прави всеки от тези сървъри

  • load.unixism.net: тук стартираме ab, утилитата Apache Benchmark. Тя генерира натоварването, необходимо за тестване на нашите сървърни архитектури.
  • nginx.unixism.net: понякога искаме да стартираме повече от един екземпляр на сървърна програма. За целта, сървърът Nginx с подходящите настройки действа като балансировчик на натоварването, идваща от ab нашите сървърни процеси.
  • zerohttpd.unixism.net: тук стартираме нашите сървърни програми на седем различни архитектури, една по една.
  • redis.unixism.net: на този сървър работи демонът Redis, където се съхраняват записите в гостната книга и броячът на посетителите.

Всички сървъри работят на едно ядро на процесора. Идеята е да се оцени максималната производителност на всяка от архитектурите. Понеже всички сървърни програми се тестват на едно и също оборудване, това е базовото ниво за тяхното сравнение. Моята тестова конфигурация се състои от виртуални сървъри, наети от Digital Ocean.

Какво измерваме?

Можем да измерим различни показатели. Оценяваме производителността на всяка архитектура в тази конфигурация, натоварвайки сървърите с заявки на различни нива на паралелизъм: натоварването нараства от 20 до 15 000 успоредни потребители.

Резултати от тестовете

На следващата диаграма е показана производителността на сървърите на различна архитектура при различни нива на паралелизъм. По ос Y — брой заявки в секунда, по ос X — паралелни връзки.

Производителност на мрежови приложения Linux. Въведение

Производителност на мрежови приложения Linux. Въведение

Производителност на мрежови приложения Linux. Въведение

По-долу е таблица с резултатите.

заявки в секунда

паралелизъм
итеративен
форка
пре-форк
потоков
пре-потоков
poll
epoll

20
7
112
2100
1800
2250
1900
2050

50
7
190
2200
1700
2200
2000
2000

100
7
245
2200
1700
2200
2150
2100

200
7
330
2300
1750
2300
2200
2100

300

380
2200
1800
2400
2250
2150

400

410
2200
1750
2600
2000
2000

500

440
2300
1850
2700
1900
2212

600

460
2400
1800
2500
1700
2519

700

460
2400
1600
2490
1550
2607

800

460
2400
1600
2540
1400
2553

900

460
2300
1600
2472
1200
2567

1000

475
2300
1700
2485
1150
2439

1500

490
2400
1550
2620
900
2479

2000

350
2400
1400
2396
550
2200

2500

280
2100
1300
2453
490
2262

3000

280
1900
1250
2502
огромен диапазон
2138

5000

огромен диапазон
1600
1100
2519

2235

8000


1200
огромен диапазон
2451

2100

10 000


огромен диапазон

2200

2200

11 000




2200

2122

12 000




970

1958

13 000




730

1897

14 000




590

1466

15 000




532

1281

От графиката и таблицата е видно, че при над 8000 успоредни заявки остават само двама играчи: пре-форк и epoll. С увеличаването на натоварването, сървърът на базата на poll работи по-зле от потоковия. Архитектурата с предварително създадени потоци създава достойна конкуренция на epoll: това е свидетелство колко добре ядрото на Linux планира голямо количество потоци.

Изходният код на ZeroHTTPd

Изходният код на ZeroHTTPd тук. За всяка архитектура има отделен каталог.

ZeroHTTPd
│
├── 01_iterative
│   ├── main.c
├── 02_forking
│   ├── main.c
├── 03_preforking
│   ├── main.c
├── 04_threading
│   ├── main.c
├── 05_prethreading
│   ├── main.c
├── 06_poll
│   ├── main.c
├── 07_epoll
│    └── main.c
├── Makefile
├── public
│   ├── index.html
│   └── tux.png
└── templates
    └── guestbook
        └── index.html

Освен седемте директории за всички архитектури, в основната директория има още две: public и templates. В първата се намира файлът index.html и изображение от първия скрийншот. Можете да поставите и други файлове и папки там, и ZeroHTTPd трябва без проблем да предостави тези статични файлове. Ако пътят в браузъра съвпада с пътя в папката public, то ZeroHTTPd търси файла index.html в тази директория. Съдържанието на гостната книга се генерира динамично. То има само главна страница, а нейното съдържание е основано на файла ‘templates/guestbook/index.html’. В ZeroHTTPd лесно могат да се добавят динамични страници за разширяване. Идеята е, че потребителите могат да добавят шаблони в тази директория и да разширяват ZeroHTTPd при необходимост.

За изграждането на всичките седем сървъра стартирайте make all от основната директория — и всички билдове ще се появят в тази директория. Изпълнимите файлове търсят директориите public и templates в директорията, от която се стартират.

Linux API

За да разберете информацията в този цикъл от статии, не е задължително да имате добро познание по Linux API. Въпреки това, препоръчвам да прочетете повече по темата, в интернет има много справочни ресурси. Въпреки че ще се докоснем до няколко категории на Linux API, нашето внимание ще бъде съсредоточено основно върху процеси, нишки, събития и мрежов стек. Освен книги и статии за Linux API, препоръчвам също да прочетете мановете за системни извиквания и използвани библиотечни функции.

Производителност и мащабируемост

Едно забележка относно производителността и мащабируемостта. Теоретично между тях няма никаква връзка. Може да имате уеб услуга, която работи прекрасно, с време за реакция от няколко милисекунди, но изобщо не е мащабируема. По същия начин, може да има уеб приложение, което работи зле и изисква няколко секунди за отговор, но е мащабируемо за десетки, за да обработва десетки хиляди едновременни потребители. Все пак, комбинацията от висока производителност и мащабируемост е много мощна. Високопродуктивните приложения обикновено икономично използват ресурси и по този начин ефективно обслужват повече едновременни потребители на сървъра, намалявайки разходите.

Задачи на CPU и I/O

В изчисленията винаги има два основни типа задачи: I/O и CPU. Получаване на заявки чрез интернет (мрежов вход-изход), обслужване на файлове (мрежов и дисков вход-изход), комуникация с база данни (мрежов и дисков вход-изход) — всичко това са действия I/O. Някои заявки към БД могат да натоварят малко CPU (сортиране, изчисляване на средната стойност на милион резултата и т.н.). Повечето уеб приложения са ограничени по максимално възможния I/O, а процесорът рядко се използва на пълна мощност. Когато видите, че в някаква задача за вход-изход се използва много CPU, вероятно е признак за лоша архитектура на приложението. Това може да означава, че ресурсите на CPU се изразходват за управление на процеси и превключване на контекста — и това не е особено полезно. Ако правите нещо като обработка на изображения, преобразуване на аудиофайлове или машинно обучение, тогава приложението изисква мощни ресурси на CPU. Но за повечето приложения това не е така.

По-подробно за сървърните архитектури

  1. Част I. Итеративна архитектура
  2. Част II. Fork-сървъри
  3. Част III. Pre-fork сървъри
  4. Част IV. Сървъри с изпълнителни потоци
  5. Част V. Сървъри с предварително създадени потоци
  6. Част VI. Архитектура на базата на poll
  7. Част VII. Архитектура на базата на epoll

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster