Leistung von Linux-Netzwerkanwendungen. Einführung

Webanwendungen werden mittlerweile überall eingesetzt und unter allen Transportprotokollen nimmt HTTP den Löwenanteil ein. Beim Studium der Nuancen der Webanwendungsentwicklung achten die meisten Menschen kaum auf das Betriebssystem, auf dem diese Anwendungen tatsächlich ausgeführt werden. Die Trennung von Entwicklung (Dev) und Betrieb (Ops) verschlimmerte die Situation nur. Mit dem Aufkommen der DevOps-Kultur übernehmen Entwickler jedoch die Verantwortung für die Ausführung ihrer Anwendungen in der Cloud. Daher ist es für sie sehr nützlich, sich gründlich mit dem Backend des Betriebssystems vertraut zu machen. Dies ist besonders nützlich, wenn Sie versuchen, ein System für Tausende oder Zehntausende gleichzeitiger Verbindungen bereitzustellen.

Die Einschränkungen bei Webdiensten sind denen anderer Anwendungen sehr ähnlich. Ob Load Balancer oder Datenbankserver, alle diese Anwendungen haben in einer Hochleistungsumgebung ähnliche Probleme. Wenn Sie diese grundlegenden Einschränkungen verstehen und wissen, wie Sie sie im Allgemeinen überwinden können, können Sie die Leistung und Skalierbarkeit Ihrer Webanwendungen bewerten.

Ich schreibe diese Artikelserie als Antwort auf Fragen junger Entwickler, die gut informierte Systemarchitekten werden möchten. Es ist unmöglich, die Optimierungstechniken für Linux-Anwendungen klar zu verstehen, ohne in die Grundlagen ihrer Funktionsweise auf Betriebssystemebene einzutauchen. Obwohl es viele Arten von Anwendungen gibt, möchte ich in dieser Serie webbasierte Anwendungen und nicht Desktop-Anwendungen wie einen Browser oder einen Texteditor untersuchen. Dieses Material richtet sich an Entwickler und Architekten, die verstehen möchten, wie Linux- oder Unix-Programme funktionieren und wie sie für eine hohe Leistung strukturiert werden.

Linux ist Serverraum Betriebssystem und in den meisten Fällen werden Ihre Anwendungen auf diesem Betriebssystem ausgeführt. Obwohl ich „Linux“ sage, können Sie meistens davon ausgehen, dass ich alle Unix-ähnlichen Betriebssysteme im Allgemeinen meine. Allerdings habe ich den zugehörigen Code nicht auf anderen Systemen getestet. Wenn Sie sich also für FreeBSD oder OpenBSD interessieren, können Ihre Ergebnisse variieren. Wenn ich etwas Linux-spezifisches ausprobiere, weise ich darauf hin.

Sie können dieses Wissen zwar nutzen, um eine App von Grund auf zu erstellen und sie perfekt zu optimieren, es ist jedoch besser, dies nicht zu tun. Wenn Sie einen neuen Webserver in C oder C++ für die Geschäftsanwendung Ihres Unternehmens schreiben, ist dies möglicherweise Ihr letzter Arbeitstag. Die Kenntnis der Struktur dieser Anwendungen hilft jedoch bei der Auswahl vorhandener Programme. Sie können prozessbasierte Systeme mit threadbasierten Systemen sowie ereignisbasierten Systemen vergleichen. Sie werden verstehen und schätzen, warum Nginx eine bessere Leistung als Apache httpd erbringt und warum eine Tornado-basierte Python-Anwendung mehr Benutzer bedienen kann als eine Django-basierte Python-Anwendung.

ZeroHTTPd: Lerntool

ZeroHTTPd ist ein Webserver, den ich als Lehrmittel von Grund auf in C geschrieben habe. Es gibt keine externen Abhängigkeiten, einschließlich Zugriff auf Redis. Wir führen unsere eigenen Redis-Verfahren aus. Weitere Einzelheiten finden Sie weiter unten.

Obwohl wir die Theorie ausführlich diskutieren könnten, gibt es nichts Besseres, als Code zu schreiben, ihn auszuführen und alle Serverarchitekturen miteinander zu vergleichen. Dies ist die naheliegendste Methode. Daher werden wir einen einfachen ZeroHTTPd-Webserver schreiben, der jedes Modell verwendet: prozessbasiert, threadbasiert und ereignisbasiert. Schauen wir uns jeden dieser Server an und sehen, wie sie im Vergleich zueinander abschneiden. ZeroHTTPd ist in einer einzigen C-Datei implementiert. Der ereignisbasierte Server umfasst utash, eine großartige Hash-Tabellen-Implementierung, die in einer einzigen Header-Datei geliefert wird. In anderen Fällen gibt es keine Abhängigkeiten, um das Projekt nicht zu verkomplizieren.

Der Code enthält viele Kommentare, die Ihnen das Verständnis erleichtern. Als einfacher Webserver mit wenigen Codezeilen ist ZeroHTTPd auch ein minimales Framework für die Webentwicklung. Die Funktionalität ist begrenzt, aber es ist in der Lage, statische Dateien und sehr einfache „dynamische“ Seiten bereitzustellen. Ich muss sagen, dass ZeroHTTPd gut geeignet ist, um zu lernen, wie man leistungsstarke Linux-Anwendungen erstellt. Im Großen und Ganzen warten die meisten Webdienste auf Anfragen, prüfen diese und bearbeiten sie. Genau das wird ZeroHTTPd tun. Dies ist ein Werkzeug zum Lernen, nicht zum Produzieren. Es ist nicht besonders gut in der Fehlerbehandlung und verfügt wahrscheinlich nicht über die besten Sicherheitspraktiken (oh ja, ich habe es verwendet). strcpy) oder die cleveren Tricks der C-Sprache. Aber ich hoffe, dass es seine Aufgabe gut erfüllt.

Leistung von Linux-Netzwerkanwendungen. Einführung
ZeroHTTPd-Homepage. Es kann verschiedene Dateitypen einschließlich Bilder ausgeben

Gästebuchanwendung

Moderne Webanwendungen sind in der Regel nicht auf statische Dateien beschränkt. Sie haben komplexe Interaktionen mit verschiedenen Datenbanken, Caches usw. Deshalb werden wir eine einfache Webanwendung namens „Gästebuch“ erstellen, in der Besucher Einträge unter ihrem Namen hinterlassen. Das Gästebuch speichert zuvor hinterlassene Einträge. Am Ende der Seite befindet sich außerdem ein Besucherzähler.

Leistung von Linux-Netzwerkanwendungen. Einführung
Webanwendung „Gästebuch“ ZeroHTTPd

Die Besucherzähler und Gästebucheinträge werden in Redis gespeichert. Für die Kommunikation mit Redis sind eigene Verfahren implementiert; sie sind nicht auf die externe Bibliothek angewiesen. Ich bin kein großer Fan davon, Homebrew-Code einzuführen, wenn es öffentlich verfügbare und gut getestete Lösungen gibt. Der Zweck von ZeroHTTPd besteht jedoch darin, die Linux-Leistung und den Zugriff auf externe Dienste zu untersuchen, während die Bereitstellung von HTTP-Anfragen erhebliche Auswirkungen auf die Leistung hat. Wir müssen die Kommunikation mit Redis in jeder unserer Serverarchitekturen vollständig kontrollieren. In einigen Architekturen verwenden wir blockierende Aufrufe, in anderen verwenden wir ereignisbasierte Verfahren. Die Verwendung einer externen Redis-Clientbibliothek bietet diese Kontrolle nicht. Darüber hinaus führt unser kleiner Redis-Client nur wenige Funktionen aus (Abrufen, Festlegen und Erhöhen eines Schlüssels; Abrufen und Hinzufügen zu einem Array). Darüber hinaus ist das Redis-Protokoll äußerst elegant und einfach. Sie müssen es nicht einmal speziell unterrichten. Allein die Tatsache, dass das Protokoll die ganze Arbeit in etwa hundert Zeilen Code erledigt, zeigt, wie gut durchdacht es ist.

Die folgende Abbildung zeigt, was die Anwendung tut, wenn der Client (Browser) eine Anfrage stellt /guestbookURL.

Leistung von Linux-Netzwerkanwendungen. Einführung
So funktioniert die Gästebuchanwendung

Wenn eine Gästebuchseite ausgegeben werden muss, gibt es einen Aufruf an das Dateisystem, um die Vorlage in den Speicher einzulesen, und drei Netzwerkaufrufe an Redis. Die Vorlagendatei enthält den größten Teil des HTML-Inhalts für die Seite im obigen Screenshot. Für den dynamischen Teil des Inhalts gibt es außerdem spezielle Platzhalter: Beiträge und Besucherzähler. Wir erhalten sie von Redis, fügen sie in die Seite ein und stellen dem Kunden fertige Inhalte zur Verfügung. Der dritte Aufruf von Redis kann vermieden werden, da Redis beim Erhöhen den neuen Schlüsselwert zurückgibt. Für unseren Server, der über eine asynchrone ereignisbasierte Architektur verfügt, sind viele Netzwerkaufrufe jedoch ein guter Test für Lernzwecke. Daher verwerfen wir den Redis-Rückgabewert der Besucherzahl und fragen ihn mit einem separaten Aufruf ab.

Serverarchitekturen ZeroHTTPd

Wir erstellen sieben Versionen von ZeroHTTPd mit der gleichen Funktionalität, aber unterschiedlichen Architekturen:

  • Iterativ
  • Fork-Server (ein untergeordneter Prozess pro Anfrage)
  • Pre-Fork-Server (Pre-Fork von Prozessen)
  • Server mit Ausführungsthreads (ein Thread pro Anfrage)
  • Server mit Pre-Thread-Erstellung
  • Architekturbasiert poll()
  • Architekturbasiert epoll

Wir messen die Leistung jeder Architektur, indem wir den Server mit HTTP-Anfragen belasten. Beim Vergleich hochparalleler Architekturen steigt jedoch die Anzahl der Abfragen. Wir testen dreimal und berechnen den Durchschnitt.

Testmethodik

Leistung von Linux-Netzwerkanwendungen. Einführung
ZeroHTTPd-Lasttest-Setup

Es ist wichtig, dass beim Ausführen von Tests nicht alle Komponenten auf demselben Computer ausgeführt werden. In diesem Fall verursacht das Betriebssystem zusätzlichen Planungsaufwand, da Komponenten um die CPU konkurrieren. Die Messung des Betriebssystem-Overheads jeder der ausgewählten Serverarchitekturen ist eines der wichtigsten Ziele dieser Übung. Das Hinzufügen weiterer Variablen wird sich nachteilig auf den Prozess auswirken. Daher funktioniert die Einstellung im Bild oben am besten.

Was macht jeder dieser Server?

  • Load.unixism.net: Hier laufen wir ab, Apache Benchmark-Dienstprogramm. Es erzeugt die Last, die zum Testen unserer Serverarchitekturen erforderlich ist.
  • nginx.unixism.net: Manchmal möchten wir mehr als eine Instanz eines Serverprogramms ausführen. Dazu fungiert der Nginx-Server mit den entsprechenden Einstellungen als Load Balancer ab zu unseren Serverprozessen.
  • zerohttpd.unixism.net: Hier führen wir unsere Serverprogramme nacheinander auf sieben verschiedenen Architekturen aus.
  • redis.unixism.net: Auf diesem Server läuft der Redis-Daemon, in dem Gästebucheinträge und Besucherzähler gespeichert werden.

Alle Server laufen auf demselben Prozessorkern. Die Idee besteht darin, die maximale Leistung jeder Architektur zu bewerten. Da alle Serverprogramme auf derselben Hardware getestet werden, ist dies eine Vergleichsgrundlage. Mein Testaufbau besteht aus virtuellen Servern, die von Digital Ocean gemietet wurden.

Was messen wir?

Sie können verschiedene Indikatoren messen. Wir bewerten die Leistung jeder Architektur in einer bestimmten Konfiguration, indem wir die Server mit Anforderungen auf unterschiedlichen Parallelitätsebenen belasten: Die Last wächst von 20 auf 15 gleichzeitige Benutzer.

Testergebnisse

Das folgende Diagramm zeigt die Leistung von Servern auf verschiedenen Architekturen und auf unterschiedlichen Ebenen der Parallelität. Die y-Achse ist die Anzahl der Anfragen pro Sekunde, die x-Achse sind parallele Verbindungen.

Leistung von Linux-Netzwerkanwendungen. Einführung

Leistung von Linux-Netzwerkanwendungen. Einführung

Leistung von Linux-Netzwerkanwendungen. Einführung

Nachfolgend finden Sie eine Tabelle mit den Ergebnissen.

Anfragen pro Sekunde

Nebenläufigkeit
iterativ
Gabel
Vorgabel
Streaming
Vorab-Streaming
Umfrage
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
große Verbreitung
2138

5000
-
große Verbreitung
1600
1100
2519
-
2235

8000
-
-
1200
große Verbreitung
2451
-
2100

10
-
-
große Verbreitung
-
2200
-
2200

11
-
-
-
-
2200
-
2122

12
-
-
-
-
970
-
1958

13
-
-
-
-
730
-
1897

14
-
-
-
-
590
-
1466

15
-
-
-
-
532
-
1281

Aus der Grafik und der Tabelle ist ersichtlich, dass über 8000 gleichzeitige Anfragen nur noch zwei Spieler übrig sind: Pre-Fork und Epoll. Mit zunehmender Auslastung schneidet ein abfragebasierter Server schlechter ab als ein Streaming-Server. Die Thread-Pre-Creation-Architektur ist ein würdiger Konkurrent von Epoll, ein Beweis dafür, wie gut der Linux-Kernel eine große Anzahl von Threads plant.

ZeroHTTPd-Quellcode

ZeroHTTPd-Quellcode hier. Für jede Architektur gibt es ein eigenes Verzeichnis.

ZeroHTTPd │ ├── 01_iterative │ ├── main.c ├── 02_forking │ ├── main.c ├── 03_preforking │ ├── main.c ├── 04 _ Einfädeln │ ├── main.c ├── 05_prethreading │ ├── main.c ├── 06_poll │ ├── main.c ├── 07_epoll │ └── main.c ├── Makefile ├── public │ ├── index .html │ └── tux .png └── Vorlagen └── Gästebuch └── index.html

Neben sieben Verzeichnissen für alle Architekturen gibt es im obersten Verzeichnis zwei weitere: public und templates. Die erste enthält die Datei index.html und das Bild aus dem ersten Screenshot. Sie können dort andere Dateien und Ordner ablegen, und ZeroHTTPd sollte diese statischen Dateien ohne Probleme bereitstellen. Wenn der Pfad im Browser mit dem Pfad im öffentlichen Ordner übereinstimmt, sucht ZeroHTTPd in ​​diesem Verzeichnis nach der Datei index.html. Der Inhalt für das Gästebuch wird dynamisch generiert. Es verfügt nur über eine Homepage und sein Inhalt basiert auf der Datei „templates/guestbook/index.html“. ZeroHTTPd fügt problemlos dynamische Seiten zur Erweiterung hinzu. Die Idee ist, dass Benutzer diesem Verzeichnis Vorlagen hinzufügen und ZeroHTTPd nach Bedarf erweitern können.

Um alle sieben Server zu erstellen, führen Sie Folgendes aus: make all aus dem Verzeichnis der obersten Ebene - und alle Builds werden in diesem Verzeichnis angezeigt. Ausführbare Dateien suchen in dem Verzeichnis, von dem aus sie gestartet werden, nach den öffentlichen Verzeichnissen und den Vorlagenverzeichnissen.

Linux-API

Sie müssen sich nicht gut mit der Linux-API auskennen, um die Informationen in dieser Artikelserie zu verstehen. Ich empfehle jedoch, mehr zu diesem Thema zu lesen; es gibt viele Referenzquellen im Internet. Obwohl wir auf mehrere Kategorien von Linux-APIs eingehen, liegt unser Fokus hauptsächlich auf Prozessen, Threads, Ereignissen und dem Netzwerkstapel. Neben Büchern und Artikeln über die Linux-API empfehle ich auch die Lektüre von Mana für Systemaufrufe und verwendete Bibliotheksfunktionen.

Leistung und Skalierbarkeit

Eine Anmerkung zur Leistung und Skalierbarkeit. Theoretisch besteht kein Zusammenhang zwischen ihnen. Sie können einen Webdienst haben, der sehr gut funktioniert, mit einer Antwortzeit von wenigen Millisekunden, aber überhaupt nicht skalierbar ist. Ebenso kann es sein, dass es eine Webanwendung mit schlechter Leistung gibt, deren Reaktion einige Sekunden dauert, die sich aber um Zehntausende von gleichzeitigen Benutzern skalieren lässt. Die Kombination aus hoher Leistung und Skalierbarkeit ist jedoch eine sehr leistungsstarke Kombination. Hochleistungsanwendungen nutzen im Allgemeinen Ressourcen sparsam und bedienen so effizient mehr gleichzeitige Benutzer auf dem Server, was die Kosten senkt.

CPU- und I/O-Aufgaben

Schließlich gibt es in der Informatik immer zwei mögliche Arten von Aufgaben: für I/O und für die CPU. Das Empfangen von Anfragen über das Internet (Netzwerk-E/A), das Bereitstellen von Dateien (Netzwerk- und Festplatten-E/A) und die Kommunikation mit der Datenbank (Netzwerk- und Festplatten-E/A) sind allesamt E/A-Aktivitäten. Einige Datenbankabfragen können etwas CPU-intensiv sein (Sortierung, Mittelung einer Million Ergebnisse usw.). Die meisten Webanwendungen sind durch die maximal möglichen I/Os begrenzt und der Prozessor wird selten voll ausgelastet. Wenn Sie feststellen, dass eine E/A-Aufgabe viel CPU beansprucht, ist dies höchstwahrscheinlich ein Zeichen für eine schlechte Anwendungsarchitektur. Dies kann dazu führen, dass CPU-Ressourcen für die Prozessverwaltung und den Kontextwechsel verschwendet werden – und das ist nicht unbedingt sinnvoll. Wenn Sie beispielsweise Bildverarbeitung, Audiodateikonvertierung oder maschinelles Lernen durchführen, benötigt die Anwendung leistungsstarke CPU-Ressourcen. Bei den meisten Anwendungen ist dies jedoch nicht der Fall.

Erfahren Sie mehr über Serverarchitekturen

  1. Teil I: Iterative Architektur
  2. Teil II. Fork-Server
  3. Teil III. Pre-Fork-Server
  4. Teil IV. Server mit Ausführungsthreads
  5. Teil V. Pre-Threaded-Server
  6. Teil VI. Polbasierte Architektur
  7. Teil VII. Epoll-basierte Architektur

Source: habr.com

Kommentar hinzufügen