Webapplikasjoner brukes nÄ overalt, og blant alle transportprotokoller utgjÞr HTTP en stor del. NÄr man utforsker detaljene ved utvikling av webapplikasjoner, legger de fleste svÊrt lite vekt pÄ operativsystemet der disse applikasjonene faktisk kjÞrer. Delingen av utvikling (Dev) og drift (Ops) har bare forverret situasjonen. Men med spredningen av DevOps-kulturen begynner utviklerne Ä ta ansvar for Ä kjÞre sine applikasjoner i skyen, sÄ det er svÊrt nyttig for dem Ä bli godt kjent med backend-operativsystemet. Dette er spesielt nyttig hvis du prÞver Ä distribuere et system for tusenvis eller titusenvis av samtidige tilkoblinger.
Begrensninger i webtjenester ligner veldig pÄ begrensninger i andre applikasjoner. Enten det er lastbalanserere eller databaser, har alle disse applikasjonene lignende utfordringer i hÞyytelsesmiljÞet. à forstÄ disse grunnleggende begrensningene og mÄtene Ä omgÄ dem pÄ, vil generelt gjÞre det lettere Ä evaluere ytelsen og skalerbarheten til webapplikasjonene dine.
Jeg skriver denne serien med artikler som svar pÄ spÞrsmÄl fra unge utviklere som Þnsker Ä bli godt informerte systemarkitekter. Det er umulig Ä forstÄ metodene for optimalisering av Linux-applikasjoner klart uten Ä dykke ned i grunnleggende prinsipper for hvordan de fungerer pÄ operativsystemnivÄ. Selv om det finnes mange typer applikasjoner, Þnsker jeg Ä utforske nettverksapplikasjoner i denne syklusen, ikke skrivebordsapplikasjoner som nettlesere eller tekstredigerere. Dette materialet er rettet mot utviklere og arkitekter som Þnsker Ä forstÄ hvordan programmer pÄ Linux eller Unix fungerer og hvordan de kan struktureres for hÞy ytelse.
Linux er et server operativsystem, og oftest kjÞrer applikasjonene dine pÄ dette OS-et. Selv om jeg sier «Linux», kan du stort sett med trygghet anta at jeg refererer til alle Unix-lignende operativsystemer generelt. Likevel har jeg ikke testet den medfÞlgende koden pÄ andre systemer. SÄ hvis du er interessert i FreeBSD eller OpenBSD, kan resultatet variere. NÄr jeg prÞver noe spesifikt for Linux, vil jeg pÄpeke dette.
Selv om du kan bruke den tilegnede kunnskapen til Ä lage en applikasjon fra bunnen av, og den vil vÊre strÄlende optimalisert, er det best Ä ikke gjÞre det. Hvis du skriver en ny webserver i C eller C++ for forretningsapplikasjonen til organisasjonen din, kan det vÊre din siste dag pÄ jobben. Men kunnskap om strukturen til disse applikasjonene vil hjelpe deg Ä velge eksisterende programmer. Du vil kunne sammenligne systemer basert pÄ prosesser med systemer basert pÄ trÄder, samt basert pÄ hendelser. Du vil forstÄ og vurdere hvorfor Nginx fungerer bedre enn Apache httpd, samt hvorfor en Python-applikasjon basert pÄ Tornado kan betjene flere brukere sammenlignet med en Python-applikasjon basert pÄ Django.
ZeroHTTPd: lĂŠringsverktĂžy
 â en webserver som jeg skrev fra bunnen av i C som et lĂŠringsverktĂžy. Den har ingen eksterne avhengigheter, inkludert tilgang til Redis. Vi kjĂžrer vĂ„re egne Redis-prosedyrer. Se mer nedenfor.
Selv om vi kunne diskutere teorien i lang tid, er det ingenting bedre enn Ä skrive kode, kjÞre den og sammenligne de ulike serverarkitekturene. Dette er den mest illustrative metoden. Derfor skal vi skrive en enkel webserver, ZeroHTTPd, og bruke hver modell: basert pÄ prosesser, trÄder og hendelser. Vi vil teste hver av disse serverne og se hvordan de fungerer sammenlignet med hverandre. ZeroHTTPd implementeres i én C-fil. Serveren basert pÄ hendelser inkluderer , en utmerket implementering av en hashtabell som leveres i én header-fil. Ellers er det ingen avhengigheter for Ä unngÄ Ä komplisere prosjektet.
Koden har mange kommentarer for Ä hjelpe med forstÄelsen. Som en enkel webserver med noen fÄ linjer kode fungerer ZeroHTTPd ogsÄ som et minimalt rammeverk for webutvikling. Den har begrenset funksjonalitet, men kan levere statiske filer og veldig enkle «dynamiske» sider. Jeg mÄ si at ZeroHTTPd er godt egnet for Ä lÊre hvordan man lager hÞyytelses Linux-applikasjoner. I bunn og grunn venter de fleste webtjenester pÄ forespÞrslene, sjekker dem og behandler dem. Det er nettopp det ZeroHTTPd vil gjÞre. Det er et lÊringsverktÞy, ikke for produksjon. Den er ikke sterk pÄ feilhÄndtering og kan knapt skryte av beste sikkerhetspraksis (Ä ja, jeg brukte strcpy) eller med smÄsprÄkelige triks av C-sprÄket. Men jeg hÄper det vil gjÞre oppgaven sin godt.

Forside for ZeroHTTPd. Den kan levere forskjellige filtyper, inkludert bilder.
Gjestebok-applikasjonen
Moderne webapplikasjoner er vanligvis ikke begrenset til statiske filer. De har kompliserte interaksjoner med forskjellige databaser, cacher, etc. Derfor vil vi lage en enkel webapplikasjon kalt «Gjestebok», hvor besÞkende kan legge igjen meldinger med navnet sitt. I gjesteboken lagres tidligere meldinger. Det finnes ogsÄ en besÞksteller nederst pÄ siden.

Webapplikasjonen «Gjestebok» ZeroHTTPd
BesÞkstelleren og meldinger fra gjesteboken lagres i Redis. For kommunikasjon med Redis er det implementert egne prosedyrer som ikke er avhengige av eksterne biblioteker. Jeg er ikke en stor fan av Ä utvikle egen kode nÄr det finnes offentlige og godt testede lÞsninger. Men mÄlet med ZeroHTTPd er Ä utforske ytelsen til Linux og tilgangen til eksterne tjenester, mens hÄndtering av HTTP-forespÞrselene alvorlig pÄvirker ytelsen. Vi mÄ ha full kontroll over kommunikasjonen med Redis i hver av vÄre serverarkitekturer. I en arkitektur bruker vi blokkerende kall, i andre - hendelsesbaserte prosedyrer. Bruken av et eksternt klientbibliotek for Redis vil ikke gi en slik kontroll. I tillegg utfÞrer vÄr lille Redis-klient bare et par funksjoner (hente, sette og inkrementere en nÞkkel; hente og legge til i en array). Dessuten er protokollen til Redis eksepsjonelt elegant og enkel. Den trenger ikke spesiell opplÊring. Det faktum at hele arbeidet protokollen gjÞr, kan utfÞres pÄ omtrent hundre linjer med kode, sier noe om hvor godt det er gjennomtenkt.
I det fÞlgende bildet vises aktiviteter i applikasjonen nÄr klienten (nettleseren) sender en forespÞrsel. /guestbookURL.

Mekanismen for drift av gjestebok-applikasjonen
NÄr en side for gjesteboken skal genereres, skjer det ett kall til filsystemet for Ä lese malen inn i minnet, og tre nettverkskall til Redis. Malfilen inneholder mesteparten av HTML-innholdet for siden pÄ skjermbildet over. Det finnes ogsÄ spesielle plassholdere for den dynamiske delen av innholdet: innlegg og besÞkstelleren. Vi henter dem fra Redis, setter dem inn pÄ siden og leverer kunden det fullstendig formede innholdet. Det tredje kallet til Redis kan unngÄs, siden Redis returnerer en ny verdi for nÞkkelen nÄr den Þkes. Imidlertid, for vÄr server med en asynkron arkitektur basert pÄ hendelser, er mange nettverkskall en god trening i lÊringsÞyemed. Dermed slipper vi den returnerte verdien fra Redis om antall besÞkende og ber om den med et eget kall.
Serverarkitekturer ZeroHTTPd
Vi bygger syv versjoner av ZeroHTTPd med identisk funksjonalitet, men forskjellige arkitekturer:
- Iterativ
- Fork-server (ett dataprogramprosess per forespĂžrsel)
- Pre-fork server (forhÄndsforgrening av prosesser)
- Server med trÄder (en trÄd per forespÞrsel)
- Server med forhÄndsskaping av trÄder
- Arkitektur basert pÄ
poll() - Arkitektur basert pÄ
epoll
Vi mÄler ytelsen til hver arkitektur ved Ä belaste serveren med HTTP-forespÞrsel. Men nÄr man sammenligner arkitekturer med hÞy grad av parallellisme, Þker antallet forespÞrsel. Vi tester tre ganger og regner ut gjennomsnittet.
Testmetodologi

Oppsett for belastningstest av ZeroHTTPd
Det er viktig at ikke alle komponenter kjÞrer pÄ én maskin nÄr testene utfÞres. I sÄ fall pÄfÞrer operativsystemet ekstra kostnader for planlegging, ettersom komponentene konkurrerer om CPU-en. à mÄle operativsystemets overhead med hver av de valgte serverarkitekturene er et av de viktigste mÄlene med dette Þvelsen. à legge til flere variabler vil vÊre skadelig for prosessen. Derfor fungerer oppsettet pÄ bildet over best.
Hva gjĂžr hver av disse serverne
- load.unixism.net: her kjĂžrer vi
ab, Apache Benchmark-verktÞyet. Det genererer belastningen som kreves for Ä teste vÄre serverarkitekturer. - nginx.unixism.net: noen ganger Þnsker vi Ä kjÞre mer enn én instans av serverprogrammet. For dette fungerer Nginx-serveren med de riktige innstillingene som en belastningsfordeler som mottar ab til vÄre serverprosesser.
- zerohttpd.unixism.net: her kjÞrer vi programmene vÄre pÄ syv ulike arkitekturer, én av gangen.
- redis.unixism.net: pÄ denne serveren kjÞrer Redis-daemonen, der oppfÞringene i gjesteboken og besÞksstatistikken lagres.
Alle servere kjÞrer pÄ ett prosessorkjerne. Ideen er Ä vurdere den maksimale ytelsen til hver arkitektur. Siden alle serverprogrammer blir testet pÄ det samme utstyret, er dette et basisnivÄ for sammenligning. Min testoppsett bestÄr av virtuelle servere leid fra Digital Ocean.
Hva mÄler vi?
Vi kan mÄle ulike indikatorer. Vi evaluerer ytelsen til hver arkitektur i denne konfigurasjonen, ved Ä laste serverne med forespÞrselene pÄ forskjellige nivÄer av parallellisme: belastningen Þker fra 20 til 15 000 samtidige brukere.
Testresultater
I diagrammet nedenfor vises ytelsen til serverne pÄ forskjellige arkitekturer ved forskjellige nivÄer av parallellisme. PÄ y-aksen er antall forespÞrseler per sekund, pÄ x-aksen er parallelle tilkoblinger.



Under er tabellen med resultatene.
forespĂžrseler per sekund
parallellisme
iterativ
fork
pre-fork
trÄd-basert
pre-trÄd-basert
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 spredning
2138
5000
â
stor spredning
1600
1100
2519
â
2235
8000
â
â
1200
stor spredning
2451
â
2100
10Â 000
â
â
stor spredning
â
2200
â
2200
11Â 000
â
â
â
â
2200
â
2122
12Â 000
â
â
â
â
970
â
1958
13Â 000
â
â
â
â
730
â
1897
14Â 000
â
â
â
â
590
â
1466
15Â 000
â
â
â
â
532
â
1281
Fra grafen og tabellen kan vi se at over 8000 samtidige forespÞrseler er det bare to aktÞrer igjen: pre-fork og epoll. Med Þkende belastning fungerer serveren basert pÄ poll dÄrligere enn den trÄd-baserte. Arkitekturen med forhÄndsopprettede trÄder gir en solid konkurranse til epoll: dette vitner om hvor godt Linux-kjernen planlegger et stort antall trÄder.
Kildekode for ZeroHTTPd
Kildekode for ZeroHTTPd . For hver arkitektur er det en egen 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.htmlI tillegg til de syv katalogene for alle arkitekturer, finnes det to andre i rotkatalogen: public og templates. I den fĂžrste ligger filen index.html og bildet fra det fĂžrste skjermbildet. Du kan plassere andre filer og mapper der, og ZeroHTTPd bĂžr uten problemer servere disse statiske filene. Hvis stien i nettleseren samsvarer med stien i public-mappen, vil ZeroHTTPd sĂžke etter filen index.html i denne katalogen. Innholdet til gjesteboken genereres dynamisk. Det har bare en hovedside, og dens innhold er basert pĂ„ filen 'templates/guestbook/index.html'. Det er enkelt Ă„ legge til dynamiske sider i ZeroHTTPd for utvidelse. Ideen er at brukere kan legge til maler i denne katalogen og utvide ZeroHTTPd etter behov.
For Ă„ bygge alle de syv serverne, kjĂžr make all fra rotkatalogen - og alle byggefilene vil dukke opp i denne katalogen. KjĂžrbare filer leter etter katalogene public og templates i den katalogen de kjĂžres fra.
Linux API
For Ä forstÄ informasjonen i denne artikkelserien, trenger du ikke Ä ha en god forstÄelse av Linux API. Jeg anbefaler imidlertid Ä lese mer om emnet; det finnes mange referanser pÄ nettet. Selv om vi vil berÞre flere kategorier av Linux API, vil vÄrt fokus hovedsakelig vÊre pÄ prosesser, trÄder, hendelser og nettverksstabler. I tillegg til bÞker og artikler om Linux API, anbefaler jeg ogsÄ Ä lese man-sider for systemkall og brukte biblioteksfunksjoner.
Ytelse og skalerbarhet
En kommentar om ytelse og skalerbarhet. Teoretisk sett er det ingen sammenheng mellom dem. Du kan ha en webtjeneste som fungerer veldig bra, med responstider pÄ noen millisekunder, men som ikke skaleres i det hele tatt. PÄ samme mÄte kan det vÊre en dÄrlig fungerende webapplikasjon som tar flere sekunder Ä svare, men som skalerer til titalls for Ä hÄndtere titusener av samtidige brukere. Likevel er kombinasjonen av hÞy ytelse og skalerbarhet en veldig kraftig kombinasjon. HÞyytelsesapplikasjoner bruker generelt ressurser Þkonomisk og betjener dermed flere samtidige brukere pÄ serveren, noe som reduserer kostnadene.
CPU- og I/O-oppgaver
Til slutt, i beregningene finnes det alltid to mulige typer oppgaver: for I/O og CPU. HĂ„ndtering av forespĂžrslene via internett (nettverksinngang/utgang), filbearbeiding (nettverks og disk I/O), kommunikasjon med databasen (nettverks og disk I/O) â alt dette er I/O-oppgaver. Enkelte forespĂžrsel til databasen kan belaste CPU en smule (sortering, beregning av gjennomsnittet av millioner av resultater, osv.). De fleste webapplikasjoner er begrenset av det maksimalt mulige I/O, mens prosessoren sjeldent brukes til sin fulle kapasitet. NĂ„r du ser at en eller annen oppgave for inngang/utgang bruker mye CPU, er det mest sannsynlig et tegn pĂ„ dĂ„rlig applikasjonsarkitektur. Dette kan bety at CPU-ressursene brukes til prosesshĂ„ndtering og kontekstbytter - og det er ikke helt nyttig. Hvis du gjĂžr noe som bildebehandling, konvertering av lydfiler eller maskinlĂŠring, vil applikasjonen kreve kraftige CPU-ressurser. Men for de fleste applikasjoner er det ikke tilfelle.
Mer om serverarkitekturer
Kilde: habr.com
