Performanța aplicației de rețea Linux. Introducere

Aplicațiile web sunt acum folosite peste tot și, printre toate protocoalele de transport, HTTP ocupă partea leului. Când studiază nuanțele dezvoltării aplicațiilor web, majoritatea oamenilor acordă foarte puțină atenție sistemului de operare în care rulează efectiv aceste aplicații. Separarea dintre dezvoltare (Dev) și operațiuni (Ops) nu a făcut decât să înrăutățească situația. Dar, odată cu creșterea culturii DevOps, dezvoltatorii devin responsabili pentru rularea aplicațiilor lor în cloud, așa că este foarte util pentru ei să se familiarizeze complet cu backend-ul sistemului de operare. Acest lucru este util mai ales dacă încercați să implementați un sistem pentru mii sau zeci de mii de conexiuni simultane.

Limitările din serviciile web sunt foarte asemănătoare cu cele din alte aplicații. Fie că este vorba despre echilibrare de încărcare sau servere de baze de date, toate aceste aplicații au probleme similare într-un mediu de înaltă performanță. Înțelegerea acestor limitări fundamentale și a modului de depășire a acestora în general vă va ajuta să evaluați performanța și scalabilitatea aplicațiilor dvs. web.

Scriu această serie de articole ca răspuns la întrebările tinerilor dezvoltatori care doresc să devină arhitecți de sisteme bine informați. Este imposibil să înțelegeți clar tehnicile de optimizare a aplicațiilor Linux fără să vă scufundați în elementele de bază ale modului în care acestea funcționează la nivel de sistem de operare. Deși există multe tipuri de aplicații, în această serie vreau să explorez mai degrabă aplicațiile bazate pe web decât aplicațiile desktop, cum ar fi un browser sau un editor de text. Acest material este destinat dezvoltatorilor și arhitecților care doresc să înțeleagă cum funcționează programele Linux sau Unix și cum să le structureze pentru performanțe ridicate.

Linux este cameră cu servere sistem de operare și cel mai adesea aplicațiile dvs. rulează pe acest sistem de operare. Deși spun „Linux”, de cele mai multe ori poți presupune cu siguranță că mă refer la toate sistemele de operare asemănătoare Unix în general. Cu toate acestea, nu am testat codul însoțitor pe alte sisteme. Deci, dacă sunteți interesat de FreeBSD sau OpenBSD, rezultatul poate fi diferit. Când încerc ceva specific Linux, subliniez asta.

Deși puteți folosi aceste cunoștințe pentru a construi o aplicație de la zero și va fi perfect optimizată, cel mai bine este să nu faceți asta. Dacă scrieți un nou server web în C sau C++ pentru aplicația de afaceri a organizației dvs., aceasta poate fi ultima dvs. zi de muncă. Cu toate acestea, cunoașterea structurii acestor aplicații va ajuta la alegerea programelor existente. Veți putea compara sistemele bazate pe procese cu sisteme bazate pe fire, precum și cu cele bazate pe evenimente. Veți înțelege și aprecia de ce Nginx are performanțe mai bune decât Apache httpd, de ce o aplicație Python bazată pe Tornado poate servi mai mulți utilizatori în comparație cu o aplicație Python bazată pe Django.

ZeroHTTPd: Instrument de învățare

ZeroHTTPd este un server web pe care l-am scris de la zero în C ca instrument de predare. Nu are dependențe externe, inclusiv acces la Redis. Derulăm propriile proceduri Redis. Vezi mai jos pentru mai multe detalii.

Deși am putea discuta teorie pe larg, nu există nimic mai bun decât scrierea codului, rularea lui și compararea tuturor arhitecturilor de server între ele. Aceasta este metoda cea mai evidentă. Prin urmare, vom scrie un server web ZeroHTTPd simplu, folosind fiecare model: bazat pe proces, bazat pe fire și bazat pe evenimente. Să verificăm fiecare dintre aceste servere și să vedem cum funcționează în comparație cu celălalt. ZeroHTTPd este implementat într-un singur fișier C. Serverul bazat pe evenimente include uthash, o implementare grozavă de tabel hash care vine într-un singur fișier antet. În alte cazuri, nu există dependențe, pentru a nu complica proiectul.

Există o mulțime de comentarii în cod pentru a vă ajuta să înțelegeți. Fiind un simplu server web în câteva linii de cod, ZeroHTTPd este, de asemenea, un cadru minim pentru dezvoltarea web. Are o funcționalitate limitată, dar este capabil să servească fișiere statice și pagini „dinamice” foarte simple. Trebuie să spun că ZeroHTTPd este bun pentru a învăța cum să creezi aplicații Linux de înaltă performanță. În general, majoritatea serviciilor web așteaptă cereri, le verifică și le procesează. Este exact ceea ce va face ZeroHTTPd. Acesta este un instrument de învățare, nu de producție. Nu este grozav la tratarea erorilor și este puțin probabil să se laude cu cele mai bune practici de securitate (oh, da, am folosit strcpy) sau trucurile inteligente ale limbajului C. Dar sper să-și facă treaba bine.

Performanța aplicației de rețea Linux. Introducere
Pagina de pornire ZeroHTTPd. Poate scoate diferite tipuri de fișiere, inclusiv imagini

Aplicație pentru cartea de oaspeți

Aplicațiile web moderne nu se limitează de obicei la fișiere statice. Au interacțiuni complexe cu diverse baze de date, cache, etc. Așa că vom crea o aplicație web simplă numită „Guest Book” în care vizitatorii lasă intrări sub numele lor. Cartea de oaspeți stochează intrările rămase mai devreme. Există, de asemenea, un contor de vizitatori în partea de jos a paginii.

Performanța aplicației de rețea Linux. Introducere
Aplicația web „Cartea de oaspeți” ZeroHTTPd

Contorul de vizitatori și intrările din cartea de oaspeți sunt stocate în Redis. Pentru comunicațiile cu Redis sunt implementate proceduri proprii, acestea nu depind de biblioteca externă. Nu sunt un mare fan al lansării codului homebrew atunci când există soluții disponibile public și bine testate. Dar scopul ZeroHTTPd este de a studia performanța Linux și accesul la servicii externe, în timp ce deservirea solicitărilor HTTP are un impact serios asupra performanței. Trebuie să controlăm pe deplin comunicațiile cu Redis în fiecare dintre arhitecturile noastre de server. În unele arhitecturi folosim apeluri de blocare, în altele folosim proceduri bazate pe evenimente. Utilizarea unei biblioteci externe client Redis nu va oferi acest control. În plus, micul nostru client Redis îndeplinește doar câteva funcții (obținerea, setarea și creșterea unei chei; obținerea și adăugarea la o matrice). În plus, protocolul Redis este extrem de elegant și simplu. Nici măcar nu trebuie să-l înveți în mod special. Însuși faptul că protocolul face toată munca în aproximativ o sută de linii de cod arată cât de bine gândit este.

Următoarea figură arată ce face aplicația atunci când clientul (browserul) solicită /guestbookURL.

Performanța aplicației de rețea Linux. Introducere
Cum funcționează aplicația carte de oaspeți

Când trebuie emisă o pagină de carte de oaspeți, există un apel către sistemul de fișiere pentru a citi șablonul în memorie și trei apeluri de rețea către Redis. Fișierul șablon conține cea mai mare parte a conținutului HTML pentru pagina din captura de ecran de mai sus. Există, de asemenea, substituenți speciali pentru partea dinamică a conținutului: postări și contor de vizitatori. Le primim de la Redis, le introducem în pagină și oferim clientului conținut complet format. Al treilea apel către Redis poate fi evitat deoarece Redis returnează noua valoare a cheii atunci când este incrementată. Cu toate acestea, pentru serverul nostru, care are o arhitectură asincronă bazată pe evenimente, multe apeluri de rețea sunt un test bun pentru învățare. Deci, renunțăm la valoarea de returnare Redis a numărului de vizitatori și o interogăm cu un apel separat.

Arhitecturi de server ZeroHTTPd

Construim șapte versiuni de ZeroHTTPd cu aceeași funcționalitate, dar arhitecturi diferite:

  • Iterativ
  • Server fork (un proces copil per cerere)
  • Server pre-fork (pre-fork-ul proceselor)
  • Server cu fire de execuție (un fir per solicitare)
  • Server cu crearea pre-thread
  • Bazat pe arhitectură poll()
  • Bazat pe arhitectură epoll

Măsurăm performanța fiecărei arhitecturi prin încărcarea serverului cu solicitări HTTP. Dar când se compară arhitecturi extrem de paralele, numărul de interogări crește. Testăm de trei ori și calculăm media.

Metodologia de testare

Performanța aplicației de rețea Linux. Introducere
Configurarea testării de încărcare ZeroHTTPd

Este important ca atunci când rulați teste, toate componentele să nu ruleze pe aceeași mașină. În acest caz, sistemul de operare implică o suprasarcină suplimentară de programare, deoarece componentele concurează pentru CPU. Măsurarea supraîncărcării sistemului de operare pentru fiecare dintre arhitecturile de server selectate este unul dintre cele mai importante obiective ale acestui exercițiu. Adăugarea mai multor variabile va deveni dăunătoare procesului. Prin urmare, setarea din imaginea de mai sus funcționează cel mai bine.

Ce face fiecare dintre aceste servere?

  • load.unixism.net: Aici alergăm ab, Utilitarul Apache Benchmark. Acesta generează încărcarea necesară pentru a testa arhitecturile serverelor noastre.
  • nginx.unixism.net: Uneori dorim să rulăm mai mult de o instanță a unui program server. Pentru a face acest lucru, serverul Nginx cu setările corespunzătoare funcționează ca un echilibrator de încărcare provenit de la ab la procesele serverului nostru.
  • zerohttpd.unixism.net: Aici rulăm programele noastre de server pe șapte arhitecturi diferite, una câte una.
  • redis.unixism.net: Acest server rulează demonul Redis, unde sunt stocate intrările din cartea de oaspeți și contoarele de vizitatori.

Toate serverele rulează pe același nucleu de procesor. Ideea este de a evalua performanța maximă a fiecărei arhitecturi. Deoarece toate programele server sunt testate pe același hardware, aceasta este o bază de comparație. Configurația mea de testare constă în servere virtuale închiriate de la Digital Ocean.

Ce măsurăm?

Puteți măsura diferiți indicatori. Evaluăm performanța fiecărei arhitecturi într-o configurație dată prin încărcarea serverelor cu solicitări la diferite niveluri de paralelism: sarcina crește de la 20 la 15 de utilizatori concurenți.

Rezultatele testelor

Următorul grafic arată performanța serverelor pe diferite arhitecturi la diferite niveluri de paralelism. Axa y este numărul de solicitări pe secundă, axa x este conexiuni paralele.

Performanța aplicației de rețea Linux. Introducere

Performanța aplicației de rețea Linux. Introducere

Performanța aplicației de rețea Linux. Introducere

Mai jos este un tabel cu rezultatele.

cereri pe secundă

paralelism
iterativ
furculiţă
pre-furca
streaming
pre-streaming
sondaj
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
răspândire mare
2138

5000
-
răspândire mare
1600
1100
2519
-
2235

8000
-
-
1200
răspândire mare
2451
-
2100

10
-
-
răspândire mare
-
2200
-
2200

11
-
-
-
-
2200
-
2122

12
-
-
-
-
970
-
1958

13
-
-
-
-
730
-
1897

14
-
-
-
-
590
-
1466

15
-
-
-
-
532
-
1281

Din grafic și tabel se poate observa că peste 8000 de solicitări simultane mai avem doar doi jucători: pre-fork și epoll. Pe măsură ce sarcina crește, un server bazat pe sondaje are performanțe mai slabe decât unul de streaming. Arhitectura de pre-creare a firelor de execuție este un concurent demn de evol, o dovadă a cât de bine programează nucleul Linux un număr mare de fire.

Cod sursă ZeroHTTPd

Cod sursă ZeroHTTPd aici. Există un director separat pentru fiecare arhitectură.

ZeroHTTPd │ ├── 01_iterativ │ ├── main.c ├── 02_forking │ ├── main.c ├─── 03_preforking │── main.│── 04_ filetare │ ├── main.c ├── 05_prethreading │ ├── main.c ├── 06_poll │ ├── main.c ├── 07_epoll │ └── XNUMX_poll │ ├── main.c ├── XNUMX_epoll │ └── └── public main.c ─ ├── index .html │ └── tux . png └── șabloane └── carte de oaspeți └── index.html

În plus față de șapte directoare pentru toate arhitecturile, mai sunt două în directorul de nivel superior: public și șabloane. Primul conține fișierul index.html și imaginea din prima captură de ecran. Puteți pune alte fișiere și foldere acolo, iar ZeroHTTPd ar trebui să difuzeze acele fișiere statice fără probleme. Dacă calea din browser se potrivește cu calea din folderul public, atunci ZeroHTTPd caută fișierul index.html în acest director. Conținutul pentru cartea de oaspeți este generat dinamic. Are doar o pagină de pornire, iar conținutul său se bazează pe fișierul „templates/guestbook/index.html”. ZeroHTTPd adaugă cu ușurință pagini dinamice pentru extensie. Ideea este că utilizatorii pot adăuga șabloane în acest director și pot extinde ZeroHTTPd după cum este necesar.

Pentru a construi toate cele șapte servere, rulați make all din directorul de nivel superior - și toate versiunile vor apărea în acest director. Fișierele executabile caută directoarele publice și șabloane din directorul din care sunt lansate.

Linux API

Nu trebuie să fii bine versat în API-ul Linux pentru a înțelege informațiile din această serie de articole. Cu toate acestea, vă recomand să citiți mai multe despre acest subiect; există multe resurse de referință pe Internet. Deși vom atinge mai multe categorii de API-uri Linux, accentul nostru se va concentra în primul rând pe procese, fire de execuție, evenimente și stiva de rețea. Pe lângă cărți și articole despre API-ul Linux, recomand și citirea mana pentru apelurile de sistem și funcțiile de bibliotecă utilizate.

Performanță și scalabilitate

O notă despre performanță și scalabilitate. Teoretic, nu există nicio legătură între ele. Poți avea un serviciu web care funcționează foarte bine, cu un timp de răspuns de câteva milisecunde, dar nu se scalează deloc. De asemenea, poate exista o aplicație web cu performanțe slabe, care durează câteva secunde pentru a răspunde, dar se scalează cu zeci pentru a gestiona zeci de mii de utilizatori concurenți. Cu toate acestea, combinația dintre performanță ridicată și scalabilitate este o combinație foarte puternică. Aplicațiile de înaltă performanță folosesc în general resursele cu moderație și astfel deservesc eficient mai mulți utilizatori concurenți pe server, reducând costurile.

CPU și sarcini I/O

În cele din urmă, în calcul există întotdeauna două tipuri posibile de sarcini: pentru I/O și CPU. Primirea cererilor prin Internet (I/O rețea), servirea fișierelor (I/O rețea și disc), comunicarea cu baza de date (I/O rețea și disc) sunt toate activitățile I/O. Unele interogări de baze de date pot consuma puțin CPU (sortare, o medie de un milion de rezultate etc.). Majoritatea aplicațiilor web sunt limitate de I/O maxim posibil, iar procesorul este rareori folosit la capacitate maximă. Când vedeți că o sarcină I/O folosește mult CPU, cel mai probabil este un semn de arhitectură slabă a aplicației. Acest lucru poate însemna că resursele CPU sunt irosite pentru gestionarea proceselor și schimbarea contextului - și acest lucru nu este în întregime util. Dacă faceți ceva precum procesarea imaginilor, conversia fișierelor audio sau învățarea automată, atunci aplicația necesită resurse puternice ale procesorului. Dar pentru majoritatea aplicațiilor nu este cazul.

Aflați mai multe despre arhitecturile de server

  1. Partea I: Arhitectura iterativă
  2. Partea a II-a. Servere cu furcă
  3. Partea a III-a. Servere pre-fork
  4. Partea a IV-a. Servere cu fire de executie
  5. Partea V. Servere pre-threaded
  6. Partea a VI-a. Arhitectură bazată pe Pol
  7. Partea a VII-a. arhitectură bazată pe epoll

Sursa: www.habr.com

Adauga un comentariu