La performance delle applicazioni di rete Linux. Introduzione

Le applicazioni web sono ormai utilizzate ovunque e tra tutti i protocolli di trasporto, HTTP occupa una buona parte. Studiando le complessità dello sviluppo di applicazioni web, la maggior parte presta poca attenzione al sistema operativo su cui queste applicazioni vengono effettivamente eseguite. La separazione dello sviluppo (Dev) e delle operazioni (Ops) ha solo aggravato la situazione. Ma con la diffusione della cultura DevOps, gli sviluppatori iniziano a prendersi la responsabilità del lancio delle proprie applicazioni nel cloud, quindi è molto utile per loro familiarizzare a fondo con il backend del sistema operativo. Questo è particolarmente utile se stai cercando di implementare un sistema per migliaia o decine di migliaia di connessioni simultanee.

Le restrizioni nei servizi web sono molto simili a quelle in altre applicazioni. Che si tratti di bilanciatore di carico o server DB, tutte queste applicazioni presentano problemi simili in ambienti ad alte prestazioni. Comprendere queste limitazioni fondamentali e i modi per superarle in generale permetterà di valutare le prestazioni e la scalabilità delle tue applicazioni web.

Scrivo 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 immergersi nelle basi, nel modo in cui funzionano a livello di sistema operativo. Anche se ci sono molti tipi di applicazioni, in questo ciclo voglio esaminare le applicazioni di rete, non quelle desktop, come browser o editor di testo. Questo materiale è destinato a sviluppatori e architetti che vogliono capire come funzionano i programmi Linux o Unix e come strutturarli per alte prestazioni.

Linux è un sistema operativo server e più spesso le tue applicazioni funzionano proprio su questo OS. Anche se dico 'Linux', nella maggior parte del tempo puoi inferire con sicurezza che si riferisce a 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 indico.

Sebbene tu possa utilizzare le conoscenze acquisite per creare un'applicazione da zero, e sarà magnificamente ottimizzata, è meglio non farlo. Se scrivi un nuovo server web in C o C++ per l'applicazione commerciale della tua organizzazione, potrebbe essere il tuo ultimo giorno di lavoro. Tuttavia, conoscere la struttura di queste applicazioni aiuterà nella scelta di programmi già esistenti. Sarai in grado di confrontare i sistemi basati su processi con i sistemi basati su thread e anche su eventi. Capirai e apprezzerai perché Nginx funziona meglio di Apache httpd e perché un'applicazione Python basata su Tornado può gestire 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. Maggiori dettagli di seguito.

Sebbene potremmo discutere a lungo la teoria, non c'è niente di meglio che scrivere codice, eseguirlo e confrontare insieme tutte le architetture dei server. Questo è il metodo più visivo. Pertanto, scriveremo un semplice server web ZeroHTTPd, applicando ciascun modello: basato su processi, thread ed eventi. Verificheremo ciascuno di questi server e vedremo come funzionano l'uno rispetto all'altro. ZeroHTTPd è implementato in un singolo file C. Il server basato su eventi include uthash, un'eccellente implementazione di una tabella hash, fornita in un unico file di intestazione. Negli altri casi non ci sono dipendenze per non complicare il progetto.

Il codice è ricco di commenti per aiutare la comprensione. Essendo un semplice server web in poche righe di codice, ZeroHTTPd è anche un framework minimo per lo sviluppo web. Ha funzionalità limitate, ma è in grado di servire file statici e alcune semplici pagine "dinamiche". Devo dire che ZeroHTTPd è molto adatto per imparare a creare applicazioni Linux ad alte prestazioni. In generale, la maggior parte dei servizi web attende richieste, le verifica e le elabora. Questo è ciò che farà ZeroHTTPd. È uno strumento per l'apprendimento, non per la produzione. Non è robusto nella gestione degli errori e difficilmente può vantare le migliori pratiche di sicurezza (oh sì, ho usato strcpy) o con trucchi astrusi del linguaggio C. Ma spero che riesca a svolgere bene il suo compito.

La performance delle applicazioni di rete Linux. Introduzione
Pagina principale di ZeroHTTPd. Può servire diversi tipi di file, inclusi le immagini

Applicazione del libro degli ospiti

Le moderne web application non si limitano solitamente a file statici. Hanno interazioni complesse con vari database, cache, ecc. Pertanto, creeremo una semplice web application chiamata "Libro degli ospiti", dove i visitatori possono lasciare messaggi a nome loro. Nel libro degli ospiti vengono salvati i messaggi lasciati in precedenza. C'è anche un contatore di visitatori in fondo alla pagina.

La performance delle applicazioni di rete Linux. Introduzione
Web application "Libro degli ospiti" di ZeroHTTPd

Il contatore di visitatori e i messaggi del libro degli ospiti sono memorizzati in Redis. Per le comunicazioni con Redis sono state implementate procedure personalizzate, che non dipendono da librerie esterne. Non sono un grande fan di scrivere codice artigianale quando esistono soluzioni pubbliche ben testate. Ma l'obiettivo di ZeroHTTPd è studiare le prestazioni di Linux e l'accesso a servizi esterni, mentre la gestione delle richieste HTTP influisce notevolmente sulle prestazioni. Dobbiamo avere il controllo completo delle comunicazioni con Redis in ciascuna delle nostre architetture server. In un'architettura utilizziamo chiamate bloccanti, in altre procedure basate su eventi. L'uso di una libreria client esterna per Redis non ci darebbe questo controllo. Inoltre, il nostro piccolo client Redis esegue solo alcune funzioni (recupero, impostazione e incremento della chiave; recupero e aggiunta a un array). Inoltre, il protocollo Redis è estremamente elegante e semplice. Non è nemmeno necessario studiarlo specificamente. Il fatto che tutto il lavoro del protocollo venga effettuato in circa cento righe di codice dimostra quanto sia ben progettato.

Nell'immagine seguente sono mostrate le azioni dell'applicazione quando il cliente (browser) richiede /guestbookURL.

La performance delle applicazioni di rete Linux. Introduzione
Meccanismo di funzionamento dell'applicazione libro degli ospiti

Quando è necessario fornire la pagina del libro degli ospiti, viene effettuata una chiamata al sistema di file per leggere il modello in memoria e tre chiamate di rete a Redis. Il file del modello contiene gran parte del contenuto HTML per la pagina nello screenshot sopra. Ci sono anche segnaposto speciali per la parte dinamica del contenuto: le voci e il contatore dei visitatori. Li otteniamo da Redis, li inseriamo nella pagina e forniamo al cliente contenuti completamente formati. La terza chiamata a Redis può essere evitata, poiché Redis restituisce un nuovo valore della chiave quando aumenta. Tuttavia, per il nostro server con un'architettura asincrona basata su eventi, un gran numero di chiamate di rete rappresenta una buona prova a scopo didattico. Pertanto, scartiamo il valore restituito da Redis riguardo al numero di visitatori e lo richiediamo con una chiamata separata.

Architetture server ZeroHTTPd

Costruiamo sette versioni di ZeroHTTPd con funzionalità identiche, ma architetture diverse:

  • Iterativa
  • Server fork (un processo figlio per richiesta)
  • Server pre-fork (forking dei processi in anticipo)
  • Server con thread (un thread per richiesta)
  • Server con thread pre-allocati
  • Architettura basata su poll()
  • Architettura basata su epoll

Misuriamo le prestazioni di ciascuna architettura caricando il server con richieste HTTP. Ma confrontando le architetture con un alto grado di parallelismo, il numero di richieste aumenta. Testiamo tre volte e calcoliamo la media.

Metodologia di test

La performance delle applicazioni di rete Linux. Introduzione
Impostazione per il test di carico di ZeroHTTPd

È importante che durante l'esecuzione dei test tutti i componenti non siano in esecuzione su una sola macchina. In questo caso, il sistema operativo comporta costi aggiuntivi per la pianificazione, poiché i componenti competono per la CPU. Misurare le spese generali del sistema operativo con ciascuna delle architetture server selezionate è uno degli obiettivi più importanti di questo esercizio. Aggiungere un numero maggiore di variabili sarebbe controproducente per il processo. Pertanto, la configurazione nell'immagine sopra funziona meglio.

Cosa fa ciascuno di questi server

  • load.unixism.net: qui eseguiamo ab, l'utilità Apache Benchmark. Essa genera il carico necessario per testare le nostre architetture server.
  • nginx.unixism.net: a volte vogliamo avviare più di un'istanza di un programma server. A tal fine, il server Nginx con le impostazioni appropriate funziona come bilanciatore di carico per le richieste provenienti da ab ai nostri processi server.
  • zerohttpd.unixism.net: qui eseguiamo i nostri programmi server su sette architetture diverse, una alla volta.
  • redis.unixism.net: su questo server è in funzione il demone Redis, dove sono memorizzati i messaggi nel libro degli ospiti e il contatore delle visite.

Tutti i server lavorano su un singolo core del processore. L'idea è di valutare le massime prestazioni di ciascuna architettura. Poiché tutti i programmi server sono testati su un'unica attrezzatura, ciò costituisce il livello base per il loro confronto. La mia installazione di test è composta da server virtuali affittati da Digital Ocean.

Cosa misuriamo?

Possiamo misurare diversi parametri. Valutiamo le prestazioni di ciascuna architettura in questa configurazione, caricando i server con richieste a diversi livelli di parallelismo: il carico cresce da 20 a 15.000 utenti simultanei.

Risultati dei test

Nel diagramma seguente è mostrata la prestazione dei server su diverse architetture a vari livelli di parallelismo. Sull'asse y - numero di richieste al secondo, sull'asse x - connessioni parallele.

La performance delle applicazioni di rete Linux. Introduzione

La performance delle applicazioni di rete Linux. Introduzione

La performance delle applicazioni di rete Linux. Introduzione

Di seguito è riportata una tabella con i risultati.

richieste al secondo

parallelismo
iterativo
un fork
pre-fork
multithreading
pre-multithreading
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
ampia variabilità
2138

5000

ampia variabilità
1600
1100
2519

2235

8000


1200
ampia variabilità
2451

2100

10 000


ampia variabilità

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 evince che oltre 8000 richieste simultanee rimangono soltanto due contendenti: pre-fork e epoll. Con l'aumento del carico, il server basato su poll funziona peggio del multithreading. L'architettura con creazione anticipata di thread è una valida concorrente di epoll: ciò testimonia quanto bene il kernel Linux gestisca un grande numero di thread.

Codice sorgente ZeroHTTPd

Codice sorgente ZeroHTTPd qui. Per ciascuna architettura è presente una directory separata.

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.html

Oltre alle sette directory per tutte le architetture, nella directory principale ci sono altre due: public e templates. Nella prima si trova il file index.html e un'immagine del primo screenshot. È possibile inserire altri file e cartelle qui, e ZeroHTTPd dovrebbe servire questi file statici senza problemi. Se il percorso nel browser corrisponde al percorso nella cartella public, ZeroHTTPd cerca il file index.html in questa directory. Il contenuto per il libro degli ospiti viene generato dinamicamente. Ha solo la pagina principale, e il suo contenuto si basa sul file ‘templates/guestbook/index.html’. Con ZeroHTTPd è facile aggiungere pagine dinamiche per espansioni. L'idea è che gli utenti possano aggiungere modelli in questa cartella ed espandere ZeroHTTPd secondo necessità.

Per costruire tutti e sette i server, esegui make all dalla directory principale – e tutte le build appariranno in questa directory. I file eseguibili cercano le directory public e templates nella directory da cui vengono avviati.

API Linux

Per comprendere le informazioni in questo ciclo di articoli, non è necessario essere esperti di API Linux. Tuttavia, consiglio di leggere di più su questo argomento, ci sono molte risorse di riferimento disponibili online. Sebbene toccheremo diverse categorie delle API Linux, la nostra attenzione sarà focalizzata principalmente su processi, thread, eventi e lo stack di rete. Oltre a libri e articoli sulle API Linux, consiglio anche di leggere i manuali per le chiamate di sistema e le funzioni di libreria utilizzate.

Prestazioni e scalabilità

Una nota sulle prestazioni e scalabilità. Teoricamente, non c'è alcuna connessione tra di loro. Puoi avere un servizio web che funziona molto bene, con tempi di risposta di pochi millisecondi, ma che non è affatto scalabile. Allo stesso modo, un'applicazione web può avere prestazioni scarse, richiedendo alcuni secondi per rispondere, ma è scalabile per gestire decine di migliaia di utenti simultanei. Tuttavia, la combinazione di elevate prestazioni e scalabilità è una combinazione molto potente. Le applicazioni ad alte prestazioni, in generale, utilizzano le risorse in modo efficiente e, in tal modo, servono più utenti simultanei sul server, riducendo i costi.

Compiti CPU e I/O

Infine, nei calcoli ci sono sempre due tipi di problemi possibili: per I/O e CPU. L'acquisizione di richieste tramite Internet (input/output di rete), la gestione dei file (input/output di rete e disco), le comunicazioni con il database (input/output di rete e disco) — tutte queste sono operazioni di I/O. Alcune richieste al database possono caricare leggermente la CPU (ordinamento, calcolo della media di un milione di risultati, ecc.). La maggior parte delle applicazioni web ha limiti sul massimo I/O possibile, mentre la CPU viene raramente utilizzata al massimo. Quando vedi che in una certa operazione di input/output si utilizza molta CPU, è probabile che sia un segno di una cattiva architettura dell'applicazione. Questo potrebbe significare che le risorse CPU vengono spese per la gestione dei processi e il cambio di contesto — e ciò non è molto utile. Se stai facendo qualcosa come l'elaborazione delle immagini, la conversione di file audio o il machine learning, allora l'applicazione richiede potenti risorse CPU. Ma per la maggior parte delle applicazioni non è così.

Maggiori dettagli sulle architetture server

  1. Parte I. Architettura iterativa
  2. Parte II. Server fork
  3. Parte III. Server pre-fork
  4. Parte IV. Server con thread di esecuzione
  5. Parte V. Server con thread pre-creati
  6. Parte VI. Architettura basata su poll
  7. Parte VII. Architettura basata su epoll

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster