Prestazioni dell'applicazione di rete Linux. introduzione

Le applicazioni web sono ormai utilizzate ovunque e, tra tutti i protocolli di trasporto, HTTP occupa la parte del leone. Quando si studiano le sfumature dello sviluppo di applicazioni web, la maggior parte delle persone presta pochissima attenzione al sistema operativo su cui vengono effettivamente eseguite queste applicazioni. La separazione tra sviluppo (Dev) e operazioni (Ops) non ha fatto altro che peggiorare la situazione. Ma con l’avvento della cultura DevOps, gli sviluppatori stanno diventando responsabili dell’esecuzione delle proprie applicazioni nel cloud, quindi è molto utile per loro acquisire familiarità con il backend del sistema operativo. Ciò è particolarmente utile se stai tentando di distribuire un sistema per migliaia o decine di migliaia di connessioni simultanee.

Le limitazioni nei servizi Web sono molto simili a quelle di altre applicazioni. Che si tratti di bilanciatori di carico o server di database, tutte queste applicazioni presentano problemi simili in un ambiente ad alte prestazioni. Comprendere queste limitazioni fondamentali e come superarle in generale ti aiuterà a valutare le prestazioni e la scalabilità delle tue applicazioni web.

Sto scrivendo questa serie di articoli in risposta alle domande di giovani sviluppatori che vogliono diventare architetti di sistema ben informati. È impossibile comprendere chiaramente le tecniche di ottimizzazione delle applicazioni Linux senza approfondire le basi del loro funzionamento a livello del sistema operativo. Sebbene esistano molti tipi di applicazioni, in questa serie desidero esplorare le applicazioni basate sul Web piuttosto che le applicazioni desktop come un browser o un editor di testo. Questo materiale è destinato a sviluppatori e architetti che desiderano capire come funzionano i programmi Linux o Unix e come strutturarli per ottenere prestazioni elevate.

Linux lo è stanza del server sistema operativo e molto spesso le tue applicazioni vengono eseguite su questo sistema operativo. Anche se dico "Linux", la maggior parte delle volte si può tranquillamente presumere che intendo tutti i sistemi operativi simili a Unix in generale. Tuttavia, non ho testato il codice allegato su altri sistemi. Quindi, se sei interessato a FreeBSD o OpenBSD, i risultati potrebbero variare. Quando provo qualcosa di specifico per Linux, lo faccio notare.

Sebbene tu possa utilizzare questa conoscenza per creare un'app da zero e sarà perfettamente ottimizzata, è meglio non farlo. Se scrivi un nuovo server Web in C o C++ per l'applicazione aziendale della tua organizzazione, questo potrebbe essere il tuo ultimo giorno di lavoro. Tuttavia, conoscere la struttura di queste applicazioni aiuterà nella scelta dei programmi esistenti. Sarai in grado di confrontare i sistemi basati su processi con sistemi basati su thread e basati su eventi. Capirai e apprezzerai perché Nginx funziona meglio di Apache httpd, perché un'applicazione Python basata su Tornado può servire più utenti rispetto a un'applicazione Python basata su Django.

ZeroHTTPd: strumento di apprendimento

ZeroHTTPd è un server web che ho scritto da zero in C come strumento didattico. Non ha dipendenze esterne, incluso l'accesso a Redis. Eseguiamo le nostre procedure Redis. Vedi sotto per ulteriori dettagli.

Anche se potremmo discutere a lungo di teoria, non c'è niente di meglio che scrivere codice, eseguirlo e confrontare tra loro tutte le architetture server. Questo è il metodo più ovvio. Pertanto, scriveremo un semplice server Web ZeroHTTPd utilizzando ciascun modello: basato su processi, basato su thread e basato su eventi. Diamo un'occhiata a ciascuno di questi server e vediamo come si comportano rispetto agli altri. ZeroHTTPd è implementato in un singolo file C. Il server basato su eventi include utash, un'ottima implementazione della tabella hash fornita in un singolo file di intestazione. In altri casi non ci sono dipendenze, per non complicare il progetto.

Ci sono molti commenti nel codice per aiutarti a capire. Essendo un semplice server web in poche righe di codice, ZeroHTTPd è anche un framework minimale per lo sviluppo web. Ha funzionalità limitate, ma è in grado di servire file statici e pagine "dinamiche" molto semplici. Devo dire che ZeroHTTPd è utile per imparare a creare applicazioni Linux ad alte prestazioni. In generale, la maggior parte dei servizi web attende le richieste, le controlla e le elabora. Questo è esattamente ciò che farà ZeroHTTPd. Questo è uno strumento per l’apprendimento, non per la produzione. Non è eccezionale nella gestione degli errori ed è improbabile che possa vantare le migliori pratiche di sicurezza (oh sì, ho usato strcpy) o i trucchi intelligenti del linguaggio C. Ma spero che faccia bene il suo lavoro.

Prestazioni dell'applicazione di rete Linux. introduzione
Home page di ZeroHTTPd. Può produrre diversi tipi di file, comprese le immagini

Applicazione per il libro degli ospiti

Le moderne applicazioni web di solito non si limitano ai file statici. Hanno interazioni complesse con vari database, cache, ecc. Quindi creeremo una semplice applicazione web chiamata "Libro degli ospiti" in cui i visitatori lasciano voci sotto i loro nomi. Il libro degli ospiti memorizza le voci lasciate in precedenza. C'è anche un contatore dei visitatori in fondo alla pagina.

Prestazioni dell'applicazione di rete Linux. introduzione
Applicazione web "Libro degli ospiti" ZeroHTTPd

Il contatore dei visitatori e le voci del libro degli ospiti sono archiviati in Redis. Per le comunicazioni con Redis vengono implementate procedure proprie che non dipendono dalla libreria esterna. Non sono un grande fan dell'implementazione del codice homebrew quando esistono soluzioni pubblicamente disponibili e ben testate. Ma lo scopo di ZeroHTTPd è studiare le prestazioni di Linux e l'accesso ai servizi esterni, mentre servire le richieste HTTP ha un grave impatto sulle prestazioni. Dobbiamo controllare completamente le comunicazioni con Redis in ciascuna delle nostre architetture server. In alcune architetture utilizziamo il blocco delle chiamate, in altre utilizziamo procedure basate sugli eventi. L'uso di una libreria client Redis esterna non fornirà questo controllo. Inoltre, il nostro piccolo client Redis esegue solo alcune funzioni (ottenere, impostare e incrementare una chiave; ottenere e accodare a un array). Inoltre, il protocollo Redis è estremamente elegante e semplice. Non hai nemmeno bisogno di insegnarlo appositamente. Il fatto stesso che il protocollo svolga tutto il lavoro in un centinaio di righe di codice dimostra quanto sia ben pensato.

La figura seguente mostra cosa fa l'applicazione quando il client (browser) lo richiede /guestbookURL.

Prestazioni dell'applicazione di rete Linux. introduzione
Come funziona l'applicazione del libro degli ospiti

Quando è necessario pubblicare una pagina del libro degli ospiti, è necessaria una chiamata al file system per leggere il modello in memoria e tre chiamate di rete a Redis. Il file modello contiene la maggior parte del contenuto HTML della pagina nello screenshot sopra. Sono inoltre presenti segnaposto speciali per la parte dinamica del contenuto: post e contatore visitatori. Li riceviamo da Redis, li inseriamo nella pagina e forniamo al cliente contenuti completamente formati. La terza chiamata a Redis può essere evitata perché Redis restituisce il nuovo valore della chiave quando viene incrementato. Tuttavia, per il nostro server, che ha un'architettura asincrona basata su eventi, molte chiamate di rete sono un buon test a scopo di apprendimento. Quindi scartiamo il valore restituito da Redis del numero di visitatori e lo interroghiamo con una chiamata separata.

Architetture server ZeroHTTPd

Stiamo costruendo sette versioni di ZeroHTTPd con le stesse funzionalità ma architetture diverse:

  • Iterativo
  • Server fork (un processo figlio per richiesta)
  • Server pre-fork (pre-fork dei processi)
  • Server con thread di esecuzione (un thread per richiesta)
  • Server con creazione del pre-thread
  • Basato sull'architettura poll()
  • Basato sull'architettura epoll

Misuriamo le prestazioni di ciascuna architettura caricando il server con richieste HTTP. Ma quando si confrontano architetture altamente parallele, il numero di query aumenta. Proviamo tre volte e calcoliamo la media.

Metodologia di prova

Prestazioni dell'applicazione di rete Linux. introduzione
Configurazione del test di carico ZeroHTTPd

È importante che quando si eseguono i test, tutti i componenti non vengano eseguiti sulla stessa macchina. In questo caso, il sistema operativo comporta un sovraccarico di pianificazione aggiuntivo poiché i componenti competono per la CPU. Misurare il sovraccarico del sistema operativo di ciascuna delle architetture server selezionate è uno degli obiettivi più importanti di questo esercizio. L'aggiunta di più variabili diventerà dannosa per il processo. Pertanto, l'impostazione nell'immagine sopra funziona meglio.

Cosa fa ciascuno di questi server?

  • load.unixism.net: qui è dove corriamo ab, Utilità Apache Benchmark. Genera il carico necessario per testare le nostre architetture server.
  • nginx.unixism.net: a volte vogliamo eseguire più di un'istanza di un programma server. Per fare ciò, il server Nginx con le impostazioni appropriate funziona come un bilanciatore del carico proveniente da ab ai processi del nostro server.
  • zerohttpd.unixism.net: Qui eseguiamo i nostri programmi server su sette diverse architetture, una alla volta.
  • redis.unixism.net: questo server esegue il demone Redis, dove sono archiviate le voci del libro degli ospiti e i contatori dei visitatori.

Tutti i server funzionano sullo stesso core del processore. L'idea è quella di valutare le massime prestazioni di ciascuna architettura. Poiché tutti i programmi server vengono testati sullo stesso hardware, questa è una base di confronto. La mia configurazione di test è costituita da server virtuali noleggiati da Digital Ocean.

Cosa stiamo misurando?

Puoi misurare diversi indicatori. Valutiamo le prestazioni di ciascuna architettura in una determinata configurazione caricando i server con richieste a diversi livelli di parallelismo: il carico cresce da 20 a 15 utenti simultanei.

Risultati del test

Il grafico seguente mostra le prestazioni dei server su diverse architetture a diversi livelli di parallelismo. L'asse y è il numero di richieste al secondo, l'asse x rappresenta le connessioni parallele.

Prestazioni dell'applicazione di rete Linux. introduzione

Prestazioni dell'applicazione di rete Linux. introduzione

Prestazioni dell'applicazione di rete Linux. introduzione

Di seguito una tabella con i risultati.

richieste al secondo

parallelismo
iterativo
forchetta
pre-forcella
streaming
pre-streaming
sondaggio
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
grande diffusione
2138

5000
-
grande diffusione
1600
1100
2519
-
2235

8000
-
-
1200
grande diffusione
2451
-
2100

10 000
-
-
grande diffusione
-
2200
-
2200

11 000
-
-
-
-
2200
-
2122

12 000
-
-
-
-
970
-
1958

13 000
-
-
-
-
730
-
1897

14 000
-
-
-
-
590
-
1466

15 000
-
-
-
-
532
-
1281

Dal grafico e dalla tabella si vede che sopra le 8000 richieste simultanee restano solo due player: pre-fork ed epoll. All'aumentare del carico, un server basato su poll funziona peggio di uno di streaming. L'architettura di pre-creazione dei thread è un degno concorrente di epoll, una testimonianza di quanto bene il kernel Linux pianifichi un gran numero di thread.

Codice sorgente ZeroHTTPd

Codice sorgente ZeroHTTPd qui. Esiste una directory separata per ciascuna architettura.

ZeroHTTPd │ ├── 01_iterativo │ ├── 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 │ ├── indice .html │ └── tux .png └── modelli └── libro degli ospiti └── indice.html

Oltre alle sette directory per tutte le architetture, ce ne sono altre due nella directory di livello superiore: public e templates. Il primo contiene il file index.html e l'immagine del primo screenshot. Puoi inserire altri file e cartelle lì e ZeroHTTPd dovrebbe fornire quei file statici senza problemi. Se il percorso nel browser corrisponde al percorso nella cartella pubblica, ZeroHTTPd cerca il file index.html in questa directory. Il contenuto del libro degli ospiti viene generato dinamicamente. Ha solo una home page e il suo contenuto è basato sul file 'templates/guestbook/index.html'. ZeroHTTPd aggiunge facilmente pagine dinamiche per l'estensione. L'idea è che gli utenti possano aggiungere modelli a questa directory ed estendere ZeroHTTPd secondo necessità.

Per costruire tutti e sette i server, esegui make all dalla directory di livello superiore e tutte le build verranno visualizzate in questa directory. I file eseguibili cercano le directory pubbliche e dei modelli nella directory da cui vengono avviati.

API Linux

Non è necessario essere esperti dell'API Linux per comprendere le informazioni contenute in questa serie di articoli. Tuttavia, ti consiglio di leggere di più su questo argomento; ci sono molte risorse di riferimento su Internet. Anche se toccheremo diverse categorie di API Linux, la nostra attenzione si concentrerà principalmente su processi, thread, eventi e stack di rete. Oltre ai libri e agli articoli sulle API Linux, consiglio anche di leggere mana per le chiamate di sistema e le funzioni di libreria utilizzate.

Prestazioni e scalabilità

Una nota su prestazioni e scalabilità. Teoricamente non esiste alcuna connessione tra loro. Puoi avere un servizio web che funziona molto bene, con un tempo di risposta di pochi millisecondi, ma non è affatto scalabile. Allo stesso modo, potrebbe esserci un'applicazione web con prestazioni scadenti che impiega pochi secondi per rispondere, ma si ridimensiona di decine per gestire decine di migliaia di utenti simultanei. Tuttavia, la combinazione di prestazioni elevate e scalabilità è una combinazione molto potente. Le applicazioni ad alte prestazioni generalmente utilizzano le risorse con parsimonia e quindi servono in modo efficiente più utenti simultanei sul server, riducendo i costi.

Attività CPU e I/O

Infine, nell'informatica ci sono sempre due possibili tipologie di task: per I/O e CPU. Ricevere richieste su Internet (I/O di rete), servire file (I/O di rete e disco), comunicare con il database (I/O di rete e disco) sono tutte attività di I/O. Alcune query del database possono richiedere un po' di utilizzo della CPU (ordinamento, media di un milione di risultati, ecc.). La maggior parte delle applicazioni web sono limitate dal numero massimo di I/O possibili e il processore viene raramente utilizzato a piena capacità. Quando noti che alcune attività di I/O utilizzano molta CPU, è molto probabile che sia un segno di un'architettura dell'applicazione inadeguata. Ciò può significare che le risorse della CPU vengono sprecate nella gestione dei processi e nel cambio di contesto, e questo non è del tutto utile. Se stai facendo qualcosa come l'elaborazione di immagini, la conversione di file audio o l'apprendimento automatico, l'applicazione richiede potenti risorse della CPU. Ma per la maggior parte delle applicazioni non è così.

Ulteriori informazioni sulle architetture server

  1. Parte I: Architettura Iterativa
  2. Seconda parte. Server fork
  3. Parte III. Server pre-fork
  4. Parte IV. Server con thread di esecuzione
  5. Parte V. Server pre-thread
  6. Parte VI. Architettura basata su Pol
  7. Parte VII. architettura basata su epoll

Fonte: habr.com

Aggiungi un commento