Prestaties van Linux-netwerkapplicaties. Invoering

Webapplicaties worden nu overal gebruikt, en van alle transportprotocollen neemt HTTP het leeuwendeel in beslag. Bij het bestuderen van de nuances van de ontwikkeling van webapplicaties besteden de meeste mensen heel weinig aandacht aan het besturingssysteem waarop deze applicaties daadwerkelijk draaien. De scheiding tussen ontwikkeling (Dev) en operaties (Ops) maakte de situatie alleen maar erger. Maar met de opkomst van de DevOps-cultuur worden ontwikkelaars verantwoordelijk voor het draaien van hun applicaties in de cloud, dus het is erg handig voor hen om grondig vertrouwd te raken met de backend van het besturingssysteem. Dit is vooral handig als u een systeem probeert in te zetten voor duizenden of tienduizenden gelijktijdige verbindingen.

De beperkingen in webservices lijken sterk op die in andere applicaties. Of het nu gaat om load balancers of databaseservers, al deze applicaties hebben vergelijkbare problemen in een high-performance omgeving. Als u deze fundamentele beperkingen begrijpt en weet hoe u deze in het algemeen kunt overwinnen, kunt u de prestaties en schaalbaarheid van uw webapplicaties evalueren.

Ik schrijf deze serie artikelen als antwoord op vragen van jonge ontwikkelaars die goed geïnformeerde systeemarchitecten willen worden. Het is onmogelijk om technieken voor het optimaliseren van Linux-applicaties duidelijk te begrijpen zonder in de basis te duiken van hoe ze werken op besturingssysteemniveau. Hoewel er veel soorten applicaties zijn, wil ik in deze serie webgebaseerde applicaties verkennen in plaats van desktopapplicaties zoals een browser of teksteditor. Dit materiaal is bedoeld voor ontwikkelaars en architecten die willen begrijpen hoe Linux- of Unix-programma's werken en hoe ze moeten worden gestructureerd voor hoge prestaties.

Linux wel server ruimte besturingssysteem, en meestal draaien uw applicaties op dit besturingssysteem. Hoewel ik 'Linux' zeg, kun je er meestal gerust van uitgaan dat ik alle Unix-achtige besturingssystemen in het algemeen bedoel. Ik heb de bijbehorende code echter niet op andere systemen getest. Dus als u geïnteresseerd bent in FreeBSD of OpenBSD, kunnen uw resultaten variëren. Wanneer ik iets Linux-specifieks probeer, wijs ik erop.

Hoewel je deze kennis kunt gebruiken om een ​​app helemaal opnieuw te bouwen en deze perfect zal worden geoptimaliseerd, kun je dat het beste niet doen. Als u een nieuwe webserver in C of C++ schrijft voor de bedrijfsapplicatie van uw organisatie, kan dit uw laatste werkdag zijn. Het kennen van de structuur van deze applicaties zal echter helpen bij het kiezen van bestaande programma's. U kunt procesgebaseerde systemen vergelijken met threadgebaseerde systemen en met gebeurtenisgebaseerde systemen. Je zult begrijpen en waarderen waarom Nginx beter presteert dan Apache httpd, waarom een ​​op Tornado gebaseerde Python-applicatie meer gebruikers kan bedienen vergeleken met een op Django gebaseerde Python-applicatie.

ZeroHTTPd: leermiddel

NulHTTPd is een webserver die ik helemaal opnieuw in C heb geschreven als leermiddel. Het heeft geen externe afhankelijkheden, inclusief toegang tot Redis. Wij voeren onze eigen Redis-procedures uit. Zie hieronder voor meer details.

Hoewel we eindeloos door kunnen gaan over de theorie, gaat er niets boven het schrijven van code, het uitvoeren ervan en het vergelijken van alle serverarchitecturen. Dit is de meest voor de hand liggende methode. Daarom zullen we een eenvoudige ZeroHTTPd-webserver schrijven met behulp van elk model: procesgebaseerd, threadgebaseerd en gebeurtenisgebaseerd. Laten we elk van deze servers eens bekijken en zien hoe ze presteren in vergelijking met elkaar. ZeroHTTPd is geïmplementeerd in een enkel C-bestand. De op gebeurtenissen gebaseerde server bevat: uthash, een geweldige hashtabelimplementatie die in één headerbestand wordt geleverd. In andere gevallen zijn er geen afhankelijkheden, om het project niet ingewikkeld te maken.

Er zijn veel opmerkingen in de code om u te helpen het te begrijpen. Omdat het een eenvoudige webserver is in een paar regels code, is ZeroHTTPd ook een minimaal raamwerk voor webontwikkeling. Het heeft een beperkte functionaliteit, maar kan statische bestanden en zeer eenvoudige "dynamische" pagina's weergeven. Ik moet zeggen dat ZeroHTTPd goed is om te leren hoe je krachtige Linux-applicaties kunt maken. Over het algemeen wachten de meeste webservices op verzoeken, controleren ze en verwerken ze. Dit is precies wat ZeroHTTPd zal doen. Dit is een hulpmiddel om te leren, niet om te produceren. Het is niet geweldig in het afhandelen van fouten en het is onwaarschijnlijk dat het over de beste beveiligingspraktijken beschikt (oh ja, ik heb het gebruikt strcpy) of de slimme trucjes van de taal C. Maar ik hoop dat het zijn werk goed doet.

Prestaties van Linux-netwerkapplicaties. Invoering
ZeroHTTPd-startpagina. Het kan verschillende bestandstypen uitvoeren, inclusief afbeeldingen

Gastenboek aanvraag

Moderne webapplicaties zijn doorgaans niet beperkt tot statische bestanden. Ze hebben complexe interacties met verschillende databases, caches, enz. Daarom zullen we een eenvoudige webapplicatie maken met de naam "Gastenboek", waar bezoekers gegevens onder hun naam achterlaten. In het gastenboek worden eerder achtergelaten vermeldingen opgeslagen. Er is ook een bezoekersteller onderaan de pagina.

Prestaties van Linux-netwerkapplicaties. Invoering
Webapplicatie "Gastenboek" ZeroHTTPd

De bezoekersteller en gastenboekgegevens worden opgeslagen in Redis. Voor de communicatie met Redis worden eigen procedures geïmplementeerd, deze zijn niet afhankelijk van de externe bibliotheek. Ik ben geen grote fan van het uitrollen van homebrew-code als er openbaar beschikbare en goed geteste oplossingen zijn. Maar het doel van ZeroHTTPd is om de prestaties van Linux en de toegang tot externe services te bestuderen, terwijl het bedienen van HTTP-verzoeken een ernstige impact op de prestaties heeft. We moeten de communicatie met Redis in elk van onze serverarchitecturen volledig beheersen. In sommige architecturen gebruiken we blokkerende oproepen, in andere gebruiken we op gebeurtenissen gebaseerde procedures. Het gebruik van een externe Redis-clientbibliotheek biedt deze controle niet. Bovendien voert onze kleine Redis-client slechts een paar functies uit (een sleutel ophalen, instellen en verhogen; een array ophalen en toevoegen). Bovendien is het Redis-protocol uiterst elegant en eenvoudig. Je hoeft het niet eens speciaal te leren. Alleen al het feit dat het protocol in ongeveer honderd regels code al het werk doet, laat zien hoe goed doordacht het is.

De volgende afbeelding laat zien wat de applicatie doet wanneer de client (browser) hierom vraagt /guestbookURL.

Prestaties van Linux-netwerkapplicaties. Invoering
Hoe de gastenboekapplicatie werkt

Wanneer een gastenboekpagina moet worden uitgegeven, is er één oproep naar het bestandssysteem om de sjabloon in het geheugen te lezen en drie netwerkoproepen naar Redis. Het sjabloonbestand bevat de meeste HTML-inhoud voor de pagina in de bovenstaande schermafbeelding. Er zijn ook speciale tijdelijke aanduidingen voor het dynamische deel van de inhoud: berichten en bezoekersteller. Wij ontvangen ze van Redis, plaatsen ze in de pagina en voorzien de klant van volledig vormgegeven inhoud. De derde aanroep van Redis kan worden vermeden omdat Redis de nieuwe sleutelwaarde retourneert wanneer deze wordt verhoogd. Voor onze server, die een asynchrone, op gebeurtenissen gebaseerde architectuur heeft, zijn veel netwerkoproepen echter een goede test voor leerdoeleinden. We negeren dus de Redis-retourwaarde van het aantal bezoekers en vragen deze op met een aparte oproep.

Serverarchitecturen ZeroHTTPd

We bouwen zeven versies van ZeroHTTPd met dezelfde functionaliteit maar verschillende architecturen:

  • Iteratief
  • Fork-server (één onderliggend proces per verzoek)
  • Pre-fork-server (pre-forking van processen)
  • Server met uitvoeringsthreads (één thread per verzoek)
  • Server met pre-thread creatie
  • Architectuur gebaseerd poll()
  • Architectuur gebaseerd epoll

We meten de prestaties van elke architectuur door de server te laden met HTTP-verzoeken. Maar bij het vergelijken van zeer parallelle architecturen neemt het aantal queries toe. We testen drie keer en berekenen het gemiddelde.

Methodologie testen

Prestaties van Linux-netwerkapplicaties. Invoering
ZeroHTTPd belastingtestopstelling

Het is belangrijk dat bij het uitvoeren van tests niet alle componenten op dezelfde machine draaien. In dit geval krijgt het besturingssysteem extra planningsoverhead omdat componenten strijden om de CPU. Het meten van de overhead van het besturingssysteem van elk van de geselecteerde serverarchitecturen is een van de belangrijkste doelen van deze oefening. Het toevoegen van meer variabelen zal schadelijk zijn voor het proces. Daarom werkt de instelling in de afbeelding hierboven het beste.

Wat doet elk van deze servers?

  • load.unixism.net: Dit is waar we actief zijn ab, Apache Benchmark-hulpprogramma. Het genereert de belasting die nodig is om onze serverarchitecturen te testen.
  • nginx.unixism.net: Soms willen we meer dan één exemplaar van een serverprogramma uitvoeren. Om dit te doen, werkt de Nginx-server met de juiste instellingen als een load balancer die afkomstig is van ab voor onze serverprocessen.
  • zerohttpd.unixism.net: Hier draaien we onze serverprogramma's op zeven verschillende architecturen, één voor één.
  • redis.unixism.net: Deze server voert de Redis-daemon uit, waar gastenboekvermeldingen en bezoekerstellers worden opgeslagen.

Alle servers draaien op dezelfde processorkern. Het idee is om de maximale prestaties van elke architectuur te evalueren. Omdat alle serverprogramma's op dezelfde hardware worden getest, is dit een basis voor vergelijking. Mijn testopstelling bestaat uit virtuele servers gehuurd van Digital Ocean.

Wat meten we?

Je kunt verschillende indicatoren meten. We evalueren de prestaties van elke architectuur in een bepaalde configuratie door de servers te belasten met verzoeken op verschillende niveaus van parallellisme: de belasting groeit van 20 naar 15 gelijktijdige gebruikers.

Test resultaten

Het volgende diagram toont de prestaties van servers op verschillende architecturen op verschillende niveaus van parallellisme. Op de y-as staat het aantal verzoeken per seconde, op de x-as staan ​​parallelle verbindingen.

Prestaties van Linux-netwerkapplicaties. Invoering

Prestaties van Linux-netwerkapplicaties. Invoering

Prestaties van Linux-netwerkapplicaties. Invoering

Hieronder vindt u een tabel met de resultaten.

verzoeken per seconde

parallellisme
iteratief
vork
voorvorken
streamen
vooraf streamen
inch
epol

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
grote spreiding
2138

5000
-
grote spreiding
1600
1100
2519
-
2235

8000
-
-
1200
grote spreiding
2451
-
2100

10
-
-
grote spreiding
-
2200
-
2200

11
-
-
-
-
2200
-
2122

12
-
-
-
-
970
-
1958

13
-
-
-
-
730
-
1897

14
-
-
-
-
590
-
1466

15
-
-
-
-
532
-
1281

Uit de grafiek en de tabel blijkt duidelijk dat we boven de 8000 gelijktijdige verzoeken nog maar twee spelers over hebben: pre-fork en epoll. Naarmate de belasting toeneemt, presteert een op polls gebaseerde server slechter dan een streamingserver. De architectuur voor het maken van threads is een waardige concurrent van epoll, een bewijs van hoe goed de Linux-kernel grote aantallen threads plant.

ZeroHTTPd-broncode

ZeroHTTPd-broncode hier. Er is een aparte map voor elke architectuur.

ZeroHTTPd │ ├── 01_iteratief │ ├── main.c ├── 02_forking │ ├── main.c ├── 03_preforking │ ├── main.c ├── 04_ draadsnijden │ ├── hoofd.c ├── 05_prethreading │ ├── main.c ├── 06_poll │ ├── main.c ├── 07_epoll │ └── main.c ├── Makefile ├── public │ ├── index .html │ └── smoking .png └── sjablonen └── gastenboek └── index.html

Naast de zeven mappen voor alle architecturen zijn er nog twee in de map op het hoogste niveau: public en templates. De eerste bevat het index.html-bestand en de afbeelding van de eerste schermafbeelding. Je kunt daar andere bestanden en mappen plaatsen, en ZeroHTTPd zou die statische bestanden zonder problemen moeten aanbieden. Als het pad in de browser overeenkomt met het pad in de openbare map, zoekt ZeroHTTPd naar het index.html-bestand in deze map. De inhoud voor het gastenboek wordt dynamisch gegenereerd. Het heeft alleen een startpagina en de inhoud is gebaseerd op het bestand 'templates/guestbook/index.html'. ZeroHTTPd voegt eenvoudig dynamische pagina's toe voor uitbreiding. Het idee is dat gebruikers sjablonen aan deze map kunnen toevoegen en ZeroHTTPd indien nodig kunnen uitbreiden.

Om alle zeven servers te bouwen, voer je uit make all vanuit de map op het hoogste niveau - en alle builds zullen in deze map verschijnen. Uitvoerbare bestanden zoeken naar de openbare mappen en sjablonenmappen in de map van waaruit ze worden gestart.

Linux-API

U hoeft niet goed thuis te zijn in de Linux API om de informatie in deze artikelenreeks te begrijpen. Ik raad echter aan om meer over dit onderwerp te lezen; er zijn veel naslagwerken op internet. Hoewel we verschillende categorieën Linux API's zullen bespreken, zal onze focus vooral liggen op processen, threads, gebeurtenissen en de netwerkstack. Naast boeken en artikelen over de Linux API raad ik ook aan mana te lezen voor systeemaanroepen en gebruikte bibliotheekfuncties.

Prestaties en schaalbaarheid

Eén opmerking over prestaties en schaalbaarheid. Theoretisch bestaat er geen verband tussen beide. Je kunt een webservice hebben die heel goed werkt, met een responstijd van een paar milliseconden, maar die helemaal niet schaalt. Op dezelfde manier kan er een slecht presterende webapplicatie zijn die een paar seconden nodig heeft om te reageren, maar deze schaalt met tientallen om tienduizenden gelijktijdige gebruikers te kunnen verwerken. De combinatie van hoge prestaties en schaalbaarheid is echter een zeer krachtige combinatie. Toepassingen met hoge prestaties maken over het algemeen spaarzaam gebruik van bronnen en bedienen dus op efficiënte wijze meer gelijktijdige gebruikers op de server, waardoor de kosten worden verlaagd.

CPU- en I/O-taken

Ten slotte zijn er bij computergebruik altijd twee soorten taken mogelijk: voor I/O en CPU. Het ontvangen van verzoeken via internet (netwerk-I/O), het serveren van bestanden (netwerk- en schijf-I/O), het communiceren met de database (netwerk- en schijf-I/O) zijn allemaal I/O-activiteiten. Sommige databasequery's kunnen een beetje CPU-intensief zijn (sorteren, een miljoen resultaten middelen, enz.). De meeste webapplicaties worden beperkt door de maximaal mogelijke I/O, en de processor wordt zelden op volle capaciteit gebruikt. Als u ziet dat een I/O-taak veel CPU gebruikt, is dit hoogstwaarschijnlijk een teken van een slechte applicatiearchitectuur. Dit kan betekenen dat CPU-bronnen worden verspild aan procesbeheer en contextwisseling - en dit is niet helemaal nuttig. Als je iets doet als beeldverwerking, conversie van audiobestanden of machine learning, dan heeft de applicatie krachtige CPU-bronnen nodig. Maar voor de meeste toepassingen is dit niet het geval.

Meer informatie over serverarchitecturen

  1. Deel I: Iteratieve architectuur
  2. Deel II. Vork-servers
  3. Deel III. Pre-fork-servers
  4. Deel IV. Servers met uitvoeringsdraden
  5. Deel V. Pre-threaded servers
  6. Deel VI. Op Pol gebaseerde architectuur
  7. Deel VII. op epoll gebaseerde architectuur

Bron: www.habr.com

Voeg een reactie