Haben Sie schon einmal einen Server im Labor einem Benchmarking unterzogen und ihn dann für realen Datenverkehr eingesetzt, nur um dann festzustellen, dass er nicht annähernd die Benchmark-Leistung erreichen kann? Die CPU-Auslastung ist gering und es sind viele freie Ressourcen vorhanden, dennoch beschweren sich die Clients über langsame Reaktionszeiten und Sie wissen nicht, wie Sie die Serverauslastung verbessern können.
Was Sie beobachten, ist ein Effekt dessen, was wir „HTTP Heavy Lifting“ nennen können. In diesem Blogbeitrag untersuchen wir, wie HTTP funktioniert und wie gängige HTTP-Server HTTP-Transaktionen verarbeiten. Wir sehen uns einige der Leistungsprobleme an, die auftreten können, und sehen, wie das ereignisgesteuerte Modell von NGINX es zu einem sehr effektiven Beschleunigungsproxy für diese HTTP-Server macht. Mit NGINX können Sie Ihre tatsächliche Leistung steigern, sodass sie wieder das Niveau Ihrer lokalen Benchmarks erreicht.
Hinweise zum Optimieren von Linux und NGINX zur Verbesserung der Geschwindigkeit und Skalierbarkeit Ihrer Anwendungen finden Sie unter „Tuning von NGINX für die Leistung“ in unserem Blog.
HTTP-Keepalive-Verbindungen sind eine notwendige Leistungsfunktion, die die Latenz reduziert und ein schnelleres Laden von Webseiten ermöglicht.
HTTP ist ein einfaches, textbasiertes Protokoll. Wenn Sie dies noch nicht getan haben, werfen Sie einen Blick auf die Ausgabe eines HTTP-Debugging-Tools, beispielsweise des Tools in Ihrem Webbrowser, und sehen Sie sich die Standardstruktur für Anfragen und Antworten an:
In der einfachsten Implementierung erstellt ein HTTP-Client eine neue TCP-Verbindung zum Zielserver, schreibt die Anfrage und empfängt die Antwort. Anschließend schließt der Server die TCP-Verbindung, um Ressourcen freizugeben.
Insbesondere bei komplexen Webseiten mit vielen Elementen oder langsamen Netzwerkverbindungen kann diese Vorgehensweise sehr ineffizient sein. Zum Erstellen einer neuen TCP-Verbindung ist ein „Drei-Wege-Handshake“ erforderlich, und zum Trennen der Verbindung ist zusätzlich ein Zwei-Wege-Shutdown-Verfahren erforderlich. Das wiederholte Erstellen und Schließen von TCP-Verbindungen, jeweils eine für jede Nachricht, ist vergleichbar mit dem Auflegen und erneuten Wählen nach jeder gesprochenen Person in einem Telefongespräch.
HTTP verwendet einen Mechanismus namens Keepalive-Verbindungen, um die TCP-Verbindung zwischen dem Client und dem Server nach Abschluss einer HTTP-Transaktion offen zu halten. Wenn der Client eine weitere HTTP-Transaktion durchführen muss, kann er die inaktive Keepalive-Verbindung verwenden, anstatt eine neue TCP-Verbindung herzustellen.
Clients öffnen im Allgemeinen mehrere gleichzeitige TCP-Verbindungen zu einem Server und führen über alle diese Verbindungen Keepalive-Transaktionen durch. Diese Verbindungen bleiben offen, bis entweder der Client oder der Server entscheidet, dass sie nicht mehr benötigt werden. Dies ist im Allgemeinen auf ein Leerlauf-Timeout zurückzuführen.
Moderne Webbrowser öffnen normalerweise sechs bis acht Keepalive-Verbindungen und halten sie mehrere Minuten lang offen, bevor sie ablaufen. Webserver können so konfiguriert werden, dass für diese Verbindungen eine Zeitüberschreitung auftritt und sie früher geschlossen werden.
Wenn viele Clients HTTP-Keepalives verwenden und beim Webserver ein Parallelitätslimit oder ein Skalierbarkeitsproblem vorliegt, sinkt die Leistung rapide, sobald dieses Limit erreicht ist.
Der oben beschriebene Ansatz zielt darauf ab, einem einzelnen Kunden die bestmögliche Leistung zu bieten. Wenn in einem Szenario wie dem der „ Tragödie der Allmende “ alle Clients auf diese Weise arbeiten, kann sich dies leider nachteilig auf die Leistung vieler gängiger Webserver und Webanwendungen auswirken.
Der Grund dafür ist, dass viele Server ein festes Parallelitätslimit haben. Beispielsweise kann der Apache-HTTP-Server in gängigen Konfigurationen nur eine begrenzte Anzahl gleichzeitiger TCP-Verbindungen verarbeiten: 150 mit dem Worker Multiprocessing Module (MPM) und 256 mit dem Prefork MPM. Jede inaktive HTTP-Keepalive-Verbindung verbraucht einen dieser Parallelitätsslots, und wenn alle Slots belegt sind, kann der Server keine weiteren HTTP-Verbindungen annehmen.
Die allgemeine Meinung besagt, dass Keepalives auf dem Webserver deaktiviert oder auf eine sehr kurze Lebensdauer begrenzt werden sollten. Sie bieten einen sehr einfachen Vektor für die Denial-of-Service- Angriffe SlowHTTPTest und Slowloris (eine schnelle Lösung finden Sie unter „Schutz vor Keep-Dead-Denial-of-Service“ auf serverfault.com).
Darüber hinaus weisen diese Web- und Anwendungsserver normalerweise jeder Verbindung einen Thread oder Prozess des Betriebssystems zu. Eine TCP-Verbindung ist ein sehr leichtes Betriebssystemobjekt, ein Thread oder Prozess hingegen ist sehr schwergewichtig. Threads und Prozesse benötigen Speicher, sie müssen aktiv vom Betriebssystem verwaltet werden und der „Kontextwechsel“ zwischen Threads oder Prozessen verbraucht CPU. Es ist äußerst ineffizient, jeder Verbindung einen eigenen Thread oder Prozess zuzuweisen.
Die große Anzahl gleichzeitiger Clientverbindungen und die Zuweisung eines Threads oder Prozesses zu jeder Verbindung führt zu dem als „HTTP Heavy Lifting“ bekannten Phänomen, bei dem für die Verarbeitung einer einfachen HTTP-Transaktion ein unverhältnismäßig großer Aufwand erforderlich ist.
Es sind nicht viele Clients erforderlich, um das Parallelitätslimit vieler moderner Web- und Anwendungsserver auszuschöpfen.
Wenn ein Client 8 TCP-Verbindungen öffnet und jede 15 Sekunden lang nach der letzten Verwendung aufrechterhält, verbraucht der Client 15 Sekunden lang 8 Parallelitätsslots. Wenn Clients Ihre Website mit einer Geschwindigkeit von 1 pro Sekunde besuchen, sind 120 Parallelitätsslots ständig durch inaktive Keepalive-Verbindungen belegt. Wenn die Rate 2 Clients pro Sekunde beträgt, werden 240 Parallelitätsslots belegt. Wenn die Slots erschöpft sind, können neue Clients keine Verbindung mehr herstellen, bis die bestehenden Verbindungen abgelaufen sind.
Dies kann zu sehr ungleichmäßigen Serviceniveaus führen. Clients, die erfolgreich eine Keepalive-Verbindung herstellen, können Ihren Dienst nach Belieben durchsuchen. Clients, die eine Verbindung herstellen möchten, wenn alle Parallelitätsslots belegt sind, werden gesperrt und müssen in einer Warteschlange warten.
Diese Probleme treten nur in langsamen Netzwerken mit vielen Clients auf. Sie treten nicht beim Benchmarking mit einem einzelnen Client über ein schnelles lokales Netzwerk auf.
Es gibt mehrere Gründe, warum diese Effekte in einem Benchmark möglicherweise nicht sichtbar sind.
Beachten Sie, dass die meisten Benchmark-Tools nur über erfolgreiche Transaktionen berichten. Verbindungen, die aufgrund erschöpfter Ressourcen ins Stocken geraten, werden möglicherweise nicht gemeldet oder machen scheinbar nur einen winzigen Bruchteil der erfolgreichen Verbindungen aus. Dies verschleiert die wahre Natur des Problems im realen Verkehr.
Jeder thread- oder prozessbasierte Web- oder Anwendungsserver ist anfällig für Parallelitätsbeschränkungen.
Dieses Problem ist jeder Web- oder Anwendungsplattform inhärent, die jeder Verbindung einen Thread oder Prozess zuweist. Dies lässt sich in einer optimierten Benchmarkumgebung nicht leicht erkennen, äußert sich jedoch in einer schlechten Leistung und übermäßigen CPU-Auslastung in einer realen Umgebung.
Sie können dieses Problem mit verschiedenen Maßnahmen beheben:
NGINX verwendet eine andere Architektur, die nicht unter den oben beschriebenen Parallelitätsproblemen leidet. Es wandelt langsame Client-Verbindungen in optimierte, Benchmark-ähnliche Verbindungen um, um die beste Leistung aus Ihren Servern herauszuholen.
NGINX verwendet ein hocheffizientes ereignisgesteuertes Modell zur Verwaltung von Verbindungen.
Jeder NGINX-Prozess kann mehrere Verbindungen gleichzeitig verarbeiten. Wenn eine neue Verbindung akzeptiert wird, ist der Overhead sehr gering (bestehend aus einem neuen Dateideskriptor und einem neuen abzufragenden Ereignis), anders als beim oben beschriebenen Pro-Prozess- oder Pro-Thread-Modell. NGINX verfügt über eine sehr effektive Ereignisschleife:
Dadurch kann jeder NGINX-Prozess problemlos auf Zehntausende oder Hunderttausende Verbindungen gleichzeitig skaliert werden.
NGINX leitet die Anforderungen dann unter Verwendung eines lokalen Pools von Keepalive-Verbindungen per Proxy an den Upstream-Server weiter. Es entsteht kein Overhead durch das Öffnen und Schließen von TCP-Verbindungen und die TCP-Stapel passen sich schnell an die optimale Fenstergröße und die Wiederholungsparameter an. Das Schreiben von Anfragen und Lesen von Antworten erfolgt über das lokale, optimierte Netzwerk wesentlich schneller:
Der Nettoeffekt besteht darin, dass der Upstream-Server über ein schnelles Netzwerk mit einem einzigen lokalen Client (NGINX) kommuniziert. Dabei handelt es sich um einen Client, der HTTP-Keepalive-Verbindungen optimal nutzt, um den Verbindungsaufbau zu minimieren, ohne Verbindungen unnötig offen zu halten. Dadurch wird der Server wieder in seine optimale, Benchmark-ähnliche Umgebung versetzt.
Wenn NGINX als HTTP-Proxy fungiert, sehen Sie:
Die Beseitigung der Last schwerer HTTP-Aufgaben ist nur eine der leistungssteigernden Maßnahmen, die NGINX für Ihre überlastete Anwendungsinfrastruktur ergreifen kann.
Die HTTP-Caching- Funktion von NGINX kann Antworten von den Upstream-Servern zwischenspeichern und dabei der Standard-Cache-Semantik folgen, um zu steuern, was zwischengespeichert wird und für wie lange. Wenn mehrere Clients dieselbe Ressource anfordern, kann NGINX aus seinem Cache antworten und belastet die Upstream-Server nicht mit doppelten Anforderungen.
NGINX kann den Upstream-Server auch von anderen Vorgängen entlasten. Sie können die Datenkomprimierungsvorgänge auslagern, um die Bandbreitennutzung zu reduzieren, die SSL/TLS-Verschlüsselung und -Entschlüsselung zentralisieren, eine anfängliche Client-Authentifizierung durchführen (z. B. mit einer HTTP-Basisauthentifizierung , Unteranforderungen an externe Authentifizierungsserver und JSON-Web-Tokens ) und alle möglichen Regeln anwenden, um bei Bedarf die Datenverkehrsrate zu begrenzen .
Vergessen Sie schließlich nicht, dass NGINX im Gegensatz zu anderen beschleunigenden Proxys, Lastverteilern oder Anwendungsbereitstellungscontrollern (ADCs) auch ein vollwertiger Webserver ist. Sie können NGINX verwenden, um statische Inhalte bereitzustellen , Datenverkehr an Anwendungsserver für Java, PHP, Python, Ruby und andere Sprachen weiterzuleiten, Medien (Audio und Video) bereitzustellen , die Integration mit Authentifizierungs- und Sicherheitssystemen durchzuführen und sogar direkt auf Transaktionen zu reagieren, indem Sie in die NGINX-Konfiguration eingebettete Regeln verwenden.
Da es keine integrierten Leistungseinschränkungen gibt, können NGINX und NGINX Plus die Hardware, auf der Sie sie einsetzen, jetzt und in Zukunft voll ausnutzen.
Um NGINX Plus auszuprobieren, starten Sie noch heute Ihre kostenlose 30-Tage-Testversion oder kontaktieren Sie uns, um Ihre Anwendungsfälle zu besprechen .
„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."