Вэб-прыкладанні цяпер выкарыстоўваюцца паўсюдна, а сярод усіх транспартных пратаколаў ільвіную дзель займае 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. Але я спадзяюся, ён добра справіцца са сваёй задачай.
Загалоўная старонка ZeroHTTPd. Ён можа выдаваць розныя тыпы файлаў, уключаючы выявы
Дадатак гасцявой кнігі
Сучасныя вэб-прыкладанні звычайна не абмежаваныя статычнымі файлы. У іх складаныя ўзаемадзеянні з рознымі БД, кэшамі і г. д. Таму мы створым просты вэб-дадатак пад назвай «Гасцінная кніга», дзе наведвальнікі пакідаюць запісы пад сваімі імёнамі. У гасцявой кнізе захоўваюцца запісы, пакінутыя раней. Ёсць таксама лічыльнік наведвальнікаў у ніжняй частцы старонкі.
Вэб-дадатак "Госьцёвая кніга" ZeroHTTPd
Лічыльнік наведвальнікаў і запісы гасцявой кнігі захоўваюцца ў Redis. Для камунікацый з Redis рэалізаваны ўласныя працэдуры, яны не залежаць ад знешняй бібліятэкі. Я не вялікі прыхільнік выкочваць дамарослы код, калі ёсць агульнадаступныя і добра пратэставаныя рашэнні. Але мэта ZeroHTTPd - вывучыць прадукцыйнасць Linux і доступ да вонкавых службаў, у той час як абслугоўванне HTTP-запытаў сур'ёзна ўплывае на прадукцыйнасць. Мы павінны цалкам кантраляваць камунікацыі з Redis у кожнай з нашых серверных архітэктур. У адной архітэктуры мы выкарыстоўваем блакавальныя выклікі, у іншых - працэдуры на аснове падзей. Выкарыстанне знешняй кліенцкай бібліятэкі Redis не дасць такі кантроль. Акрамя таго, наш маленькі кліент Redis выконвае толькі некалькі функцый (атрыманне, настройка і павелічэнне ключа; атрыманне і даданне да масіва). Да таго ж, пратакол Redis выключна элегантны і просты. Яго нават вучыць спецыяльна не трэба. Сам факт, што ўсю працу пратакол выконвае прыкладна ў ста радках кода, кажа аб тым, наколькі ён добра прадуманы.
На наступным малюнку паказаны дзеянні прыкладання, калі кліент (браўзэр) запытвае /guestbookURL.
Механізм працы прыкладання гасцявой кнігі
Калі трэба выдаць старонку гасцявой кнігі, адбываецца адзін выклік да файлавай сістэмы для чытання шаблона ў памяць і тры сеткавыя выклікі да Redis. Файл шаблону змяшчае большую частку змесціва HTML для старонкі на скрыншоце уверсе. Там ёсць таксама спецыяльныя запаўняльнікі для дынамічнай часткі кантэнту: запісаў і лічыльніка наведвальнікаў. Мы атрымліваем іх з Redis, устаўляемы на старонку і выдаём кліенту цалкам сфармаваны кантэнт. Трэцяга выкліку Redis можна пазбегнуць, паколькі Redis вяртае новае значэнне ключа пры павелічэнні. Аднак для нашага сервера з асінхроннай архітэктурай на аснове падзей мноства сеткавых выклікаў - добрае выпрабаванне ў навучальных мэтах. Такім чынам, мы адкідаем якое вяртаецца значэнне Redis аб колькасці наведвальнікаў і запытваем яго асобным выклікам.
Серверныя архітэктуры ZeroHTTPd
Мы будуем сем версій ZeroHTTPd з аднолькавай функцыянальнасцю, але рознымі архітэктурамі:
Ітэратыўная
Форк сервер (адзін даччыны працэс на запыт)
Прэ-форк сервер (папярэдні форкінг працэсаў)
Сервер c патокамі выканання (адзін thread на запыт)
Сервер з папярэднім стварэннем патокаў
Архітэктура на базе poll()
Архітэктура на базе epoll
Вымяраем прадукцыйнасць кожнай архітэктуры, загрузіўшы сервер HTTP-запытамі. Але пры параўнанні архітэктур з высокай ступенню паралелізму колькасць запытаў павялічваецца. Тэстуем тры разы і лічым сярэдняе.
Метадалогія тэсціравання
Устаноўка для нагрузачнага тэсціравання 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 - паралельныя злучэнні.
З графіка і табліцы бачна, што вышэй за 8000 адначасовых запытаў у нас застаецца толькі два гульцы: пре-форк і epoll. З ростам нагрузкі сервер на базе poll працуе горш, чым струменевае. Архітэктура з папярэднім стварэннем струменяў складае годную канкурэнцыю epoll: гэтае сведчанне, наколькі добра ядро Linux плануе вялікую колькасць струменяў.
Зыходны код ZeroHTTPd
Зыходны код ZeroHTTPd тут. Для кожнай архітэктуры асобны каталог.
Акрамя сямі дырэкторый для ўсіх архітэктур, у каталогу верхняга ўзроўня ёсць яшчэ дзве: public і templates. У першым ляжыць файл index.html і малюнак з першага скрыншота. Туды можна змясціць іншыя файлы і тэчкі, і ZeroHTTPd павінен без праблем выдаць гэтыя статычныя файлы. Калі path у браўзэры адпавядае шляхі ў тэчцы 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. Але для большасці дадаткаў гэта не так.