Webbapplikationer anvÀnds nu överallt, och bland alla transportprotokoll stÄr HTTP för en stor del. NÀr man studerar detaljerna i webbapplikationsutveckling Àgnar de flesta mycket lite uppmÀrksamhet Ät operativsystemet dÀr dessa applikationer faktiskt körs. Separationen av utveckling (Dev) och drift (Ops) har bara förvÀrrat situationen. Men med spridningen av DevOps-kulturen börjar utvecklare ta ansvar för att köra sina applikationer i molnet, sÄ det Àr mycket anvÀndbart för dem att förstÄ operativsystemets backend i detalj. Detta Àr sÀrskilt vÀrdefullt om du försöker implementera ett system för tusentals eller tiotusentals samtidiga anslutningar.
BegrÀnsningar i webbservrar liknar mycket begrÀnsningar i andra applikationer. Oavsett om det handlar om lastbalanserare eller databasservrar, har alla dessa applikationer liknande problem i högpresterande miljöer. Att förstÄ dessa grundlÀggande begrÀnsningar och sÀtt att övervinna dem kommer generellt att möjliggöra en uppskattning av prestandan och skalbarheten hos dina webbapplikationer.
Jag skriver denna serie artiklar som svar pĂ„ frĂ„gor frĂ„n unga utvecklare som vill bli vĂ€l informerade systemarkitekter. Det Ă€r omöjligt att tydligt förstĂ„ metoder för optimering av Linux-applikationer utan att dyka ner i grunderna för hur de fungerar pĂ„ operativsystemsnivĂ„. Ăven om det finns mĂ„nga typer av applikationer, vill jag i denna cykel utforska nĂ€tverksapplikationer snarare Ă€n stationĂ€ra, som webblĂ€sare eller textredigerare. Detta material riktar sig till utvecklare och arkitekter som vill förstĂ„ hur Linux- eller Unix-program fungerar och hur de struktureras för hög prestanda.
Linux Ă€r en serverbaserad operativsystem, och oftast körs dina applikationer just pĂ„ detta OS. Ăven om jag sĂ€ger "Linux", kan du nĂ€stan alltid med sĂ€kerhet anta att det syftar pĂ„ alla Unix-liknande operativsystem i allmĂ€nhet. Jag har dock inte testat den medföljande koden pĂ„ andra system. SĂ„ om du Ă€r intresserad av FreeBSD eller OpenBSD kan resultaten variera. NĂ€r jag testar nĂ„got som Ă€r specifikt för Linux, markerar jag det.
Ăven om du kan anvĂ€nda den kunskap du fĂ„r för att skapa en applikation frĂ„n grunden och att den kan bli vĂ€loptimerad, Ă€r det bĂ€st att undvika det. Om du skriver en ny webbserver i C eller C++ för ditt företags affĂ€rsapplikation, kan det bli din sista dag pĂ„ jobbet. Men att ha kunskap om strukturen hos dessa applikationer kommer att hjĂ€lpa vid valet av redan existerande program. Du kommer att kunna jĂ€mföra system baserat pĂ„ processer med system baserade pĂ„ trĂ„dar, liksom hĂ€ndelsebaserade system. Du kommer att förstĂ„ och uppskatta varför Nginx fungerar bĂ€ttre Ă€n Apache httpd, och varför en Python-applikation baserad pĂ„ Tornado kan hantera fler anvĂ€ndare jĂ€mfört med en Python-applikation baserad pĂ„ Django.
ZeroHTTPd: utbildningsverktyg
 â en webbserver som jag har skrivit frĂ„n grunden i C som ett utbildningsverktyg. Den har inga externa beroenden, inklusive tillgĂ„ng till Redis. Vi kör vĂ„ra egna Redis-rutiner. LĂ€s mer nedan.
Ăven om vi skulle kunna diskutera teorin lĂ€nge, finns det inget bĂ€ttre Ă€n att skriva kod, köra den och jĂ€mföra alla serverarkitekturer. Det Ă€r den mest visuella metoden. DĂ€rför kommer vi att skriva en enkel webbserver, ZeroHTTPd, med varje modell: baserat pĂ„ processer, trĂ„dar och hĂ€ndelser. Vi kommer att testa var och en av dessa servrar och se hur de fungerar jĂ€mfört med varandra. ZeroHTTPd Ă€r implementerad i en enda C-fil. Servern för hĂ€ndelsehantering inkluderar , en utmĂ€rkt implementation av en hash-tabell som levereras i en enda header-fil. I övrigt finns inga beroenden för att inte komplicera projektet.
Koden innehÄller mÄnga kommentarer för att hjÀlpa till att förstÄ. Som en enkel webbserver i nÄgra fÄ rader kod Àr ZeroHTTPd ocksÄ en minimal ram för webbutveckling. Den har begrÀnsad funktionalitet, men den kan leverera statiska filer och mycket enkla "dynamiska" sidor. Jag mÄste sÀga att ZeroHTTPd Àr vÀl lÀmpad för att lÀra sig hur man skapar högpresterande Linux-applikationer. I huvudsak vÀntar de flesta webbservrar pÄ förfrÄgningar, verifierar dem och bearbetar dem. Det Àr precis vad ZeroHTTPd kommer att göra. Det Àr ett verktyg för utbildning, inte för produktion. Det Àr inte bra pÄ felhantering och kommer troligen inte att skryta med bÀsta sÀkerhetspraxis (Äh ja, jag anvÀnde strcpy) eller krÄngliga tricks med C-sprÄket. Men jag hoppas att det kommer att klara uppgiften bra.

Startsidan för ZeroHTTPd. Den kan leverera olika typer av filer, inklusive bilder
GĂ€stboksapplikation
Moderna webbapplikationer Àr vanligtvis inte begrÀnsade till statiska filer. De har komplexa interaktioner med olika databaser, cachear, osv. DÀrför kommer vi att skapa en enkel webbapplikation som heter "GÀstbok", dÀr besökare lÀmnar meddelanden under sina namn. GÀstboken sparar tidigare lÀmnade meddelanden. Det finns ocksÄ en besöksrÀknare lÀngst ner pÄ sidan.

Webbapplikationen "GÀstbok" för ZeroHTTPd
BesöksrÀknaren och meddelandena i gÀstboken lagras i Redis. För kommunikation med Redis har vi implementerat egna procedurer som inte Àr beroende av externa bibliotek. Jag Àr ingen stort fan av att lansera hemgjord kod nÀr det finns offentliga och vÀl testade lösningar. Men mÄlet med ZeroHTTPd Àr att studera prestanda i Linux och tillgÄng till externa tjÀnster, medan hanteringen av HTTP-förfrÄgningar allvarligt pÄverkar prestandan. Vi mÄste ha full kontroll över kommunikationen med Redis i varje vÄr serverarkitektur. I en arkitektur anvÀnder vi blockerande anrop, i andra hÀndelsebaserade procedurer. AnvÀndning av ett externt Redis-klientbibliotek kommer inte att ge den kontroll vi behöver. Dessutom utför vÄr lilla Redis-klient bara nÄgra fÄ funktioner (hÀmta, stÀlla in och öka nyckeln; hÀmta och lÀgga till i en array). Dessutom Àr Redis-protokollet oerhört elegant och enkelt. Man behöver faktiskt inte lÀra sig det specifikt. Faktumet att hela arbetet protokollet utförs i ungefÀr hundra rader kod talar för hur vÀl genomtÀnkt det Àr.
Följande bild visar applikationens aktiviteter nÀr klienten (webblÀsaren) gör en begÀran /guestbookURL.

Hur gÀstboksapplikationen fungerar
NÀr en gÀstboksida ska returneras görs ett anrop till filsystemet för att lÀsa in mallen i minnet och tre nÀtverksanrop till Redis. Mallfilen innehÄller största delen av HTML-innehÄllet för sidan i skÀrmdumpen lÀngst upp. Det finns ocksÄ speciella platshÄllare för den dynamiska delen av innehÄllet: poster och besöksrÀknare. Vi hÀmtar dem frÄn Redis, infogar dem pÄ sidan och levererar helt fÀrdigt innehÄll till klienten. Det tredje Redis-anropet kan undvikas, eftersom Redis returnerar det nya vÀrdet för nyckeln vid varje ökning. Men för vÄr server med en asynkron hÀndelsebaserad arkitektur Àr mÄnga nÀtverksanrop en bra utmaning för utbildningssyften. DÀrför slÀpper vi det returnerade vÀrdet frÄn Redis om antalet besökare och begÀr det med ett separat anrop.
ZeroHTTPd-serverarkitekturer
Vi bygger sju versioner av ZeroHTTPd med samma funktionalitet men olika arkitekturer:
- Iterativ
- Fork-server (en underprocess per anrop)
- Pre-fork-server (förhandsforkning av processer)
- Server med trÄdar (en trÄd per anrop)
- Server med förskapat trÄdar
- Arkitektur baserad pÄ
poll() - Arkitektur baserad pÄ
epoll
Vi mÀter prestandan för varje arkitektur genom att belasta servern med HTTP-anrop. Men vid jÀmförelse av arkitekturer med hög parallellism ökar antalet anrop. Vi testar tre gÄnger och rÀknar medelvÀrdet.
Testmetodik

InstÀllning för belastningstest av ZeroHTTPd
Det Àr viktigt att vid genomförandet av testerna inte alla komponenter körs pÄ samma maskin. I sÄ fall bÀr operativsystemet pÄ extra overhead för schemalÀggning, eftersom komponenterna tÀvlar om CPU-resurser. Att mÀta operativsystemets overhead med varje av de valda serverarkitekturerna Àr ett av de mest avgörande mÄlen med denna övning. Att lÀgga till fler variabler kan vara skadligt för processen. DÀrför fungerar instÀllningen i bilden ovan bÀst.
Vad gör var och en av dessa servrar
- load.unixism.net: hÀr kör vi
ab, verktyget Apache Benchmark. Det genererar den belastning som behövs för att testa vÄra serverarkitekturer. - nginx.unixism.net: ibland vill vi köra mer Àn ett instans av serverprogrammet. För detta fungerar Nginx-servern med rÀtt instÀllningar som en belastningsbalanserare för inkommande trafik frÄn ab vÄra serverprocesser.
- zerohttpd.unixism.net: hÀr kör vi vÄra serverprogram pÄ sju olika arkitekturer, en i taget.
- redis.unixism.net: pÄ denna server körs Redis-demonen, dÀr uppgifter för gÀstboken och besökarrÀknaren lagras.
Alla servrar arbetar pÄ en enda CPU-kÀrna. Idén Àr att bedöma den maximala prestandan hos varje arkitektur. Eftersom alla serverprogram testas pÄ samma hÄrdvara, Àr detta grundnivÄn för jÀmförelse. Min testuppsÀttning bestÄr av virtuella servrar hyrda frÄn Digital Ocean.
Vad mÀter vi?
Olika mÄtt kan mÀtas. Vi bedömer prestandan hos varje arkitektur i denna konfiguration genom att belasta servrarna med förfrÄgningar pÄ olika nivÄer av parallellism: belastningen ökar frÄn 20 till 15 000 samtidiga anvÀndare.
Testresultat
I det följande diagrammet visas servernas prestanda pĂ„ olika arkitekturer vid olika nivĂ„er av parallellism. PĂ„ y-axeln â antalet förfrĂ„gningar per sekund, pĂ„ x-axeln â parallella anslutningar.



Nedan Àr tabellen med resultaten.
förfrÄgningar per sekund
parallellism
process.
fork
pre-fork
trÄdad
pre-trÄdad
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
stor spridning
2138
5000
â
stor spridning
1600
1100
2519
â
2235
8000
â
â
1200
stor spridning
2451
â
2100
10Â 000
â
â
stor spridning
â
2200
â
2200
11Â 000
â
â
â
â
2200
â
2122
12Â 000
â
â
â
â
970
â
1958
13Â 000
â
â
â
â
730
â
1897
14Â 000
â
â
â
â
590
â
1466
15Â 000
â
â
â
â
532
â
1281
FrÄn diagrammet och tabellen kan vi se att vid över 8000 samtidiga förfrÄgningar kvarstÄr bara tvÄ aktörer: pre-fork och epoll. Med ökande belastning presterar poll-baserade servrar sÀmre Àn trÄdarna. Arkitekturen med förhandskapade trÄdar Àr en vÀrdig konkurrent till epoll: detta visar hur vÀl Linux-kÀrnan planerar ett stort antal trÄdar.
KÀllkoden för ZeroHTTPd
KÀllkoden för ZeroHTTPd . För varje arkitektur finns en separat katalog.
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.htmlFörutom de sju katalogerna för alla arkitekturer finns det tvĂ„ ytterligare i rotkatalogen: public och templates. I den första finns filen index.html och en bild frĂ„n den första skĂ€rmdumpen. Dit kan man lĂ€gga andra filer och mappar, och ZeroHTTPd bör utan problem kunna leverera dessa statiska filer. Om sökvĂ€gen i webblĂ€saren motsvarar sökvĂ€gen i mappen public, letar ZeroHTTPd efter filen index.html i denna katalog. InnehĂ„llet för gĂ€stboken genereras dynamiskt. Den har bara en huvudpage, och dess innehĂ„ll baseras pĂ„ filen âtemplates/guestbook/index.htmlâ. Det Ă€r enkelt att lĂ€gga till dynamiska sidor i ZeroHTTPd för utvidgning. IdĂ©n Ă€r att anvĂ€ndare kan lĂ€gga till mallar i denna katalog och utvidga ZeroHTTPd efter behov.
För att bygga alla sju servrar, kör make all frĂ„n rotkatalogen â och alla byggnationer kommer att dyka upp i denna katalog. Exekverbara filer söker i katalogerna public och templates i den katalog frĂ„n vilken de körs.
Linux API
För att förstĂ„ informationen i denna artikelserie behöver man inte vara sĂ€rskilt insatt i Linux API. Jag rekommenderar dock att lĂ€sa mer om detta Ă€mne, det finns mĂ„nga referensresurser pĂ„ nĂ€tet. Ăven om vi kommer att beröra flera kategorier av Linux API, kommer vĂ„rt fokus huvudsakligen att ligga pĂ„ processer, trĂ„dar, hĂ€ndelser och nĂ€tverksstacken. Utöver böcker och artiklar om Linux API, rekommenderar jag Ă€ven att lĂ€sa man-sidorna för systemanrop och de anvĂ€nda biblioteksfunktionerna.
Prestanda och skalbarhet
En observation angÄende prestanda och skalbarhet. Teoretiskt sett finns det ingen koppling mellan dem. Du kan ha en webbservice som fungerar mycket bra, med svarstid pÄ nÄgra millisekunder, men den Àr överhuvudtaget inte skalbar. PÄ samma sÀtt kan det finnas en webbapplikation som fungerar dÄligt och krÀver flera sekunder för att svara, men den kan skalas till tiotals för att hantera tiotusentals samtidiga anvÀndare. Trots detta Àr en kombination av hög prestanda och skalbarhet en mycket kraftfull kombination. Högpresterande applikationer anvÀnder generellt sett resurser sparsamt och kan dÀrmed effektivt betjÀna fler samtidiga anvÀndare pÄ servern, vilket sÀnker kostnaderna.
CPU- och I/O-uppgifter
I berÀkningar finns det alltid tvÄ möjliga typer av uppgifter: för I/O och CPU. Att hÀmta förfrÄgningar via internet (nÀtverksin- och utmatning), hantera filer (nÀtverks- och disk I/O), kommunicera med databasen (nÀtverks- och disk I/O) - allt detta Àr I/O-ÄtgÀrder. Vissa databasförfrÄgningar kan nÄgot belasta CPU:n (sortering, berÀkning av genomsnittet av en miljon resultat osv.). De flesta webbapplikationer Àr begrÀnsade av maximalt möjliga I/O, medan processorn sÀllan anvÀnds pÄ full kapacitet. NÀr du ser att en viss I/O-uppgift anvÀnder mycket CPU, Àr det troligtvis ett tecken pÄ dÄlig applikationsarkitektur. Det kan innebÀra att CPU-resurser spenderas pÄ processhantering och kontextvÀxling - vilket inte Àr sÀrskilt anvÀndbart. Om du gör nÄgot som bildbehandling, konvertering av ljudfiler eller maskininlÀrning, krÀver applikationen dÄ kraftfulla CPU-resurser. Men för de flesta applikationer Àr det inte fallet.
Mer om serverarkitekturer
KĂ€lla: habr.com
