BLOG | NGINX

Einführung von HTTP/2 Server Push mit NGINX 1.13.9

NGINX-Teil-von-F5-horiz-schwarz-Typ-RGB
Owen Garrett Miniaturbild
Owen Garrett
Veröffentlicht am 20. Februar 2018

Unterstützung für HTTP/2-Server-Push ist auch in NGINX Plus R15 enthalten.

Wir freuen uns, Ihnen mitteilen zu können, dass NGINX 1.13.9 , veröffentlicht am 20. Februar 2018 , Unterstützung für HTTP/2-Server-Push bietet. Für NGINX Plus-Benutzer wird HTTP/2-Server-Push-Unterstützung in der kommenden NGINX Plus-Version R15 enthalten sein, die für April 2018 geplant ist.

Server-Push, das in der HTTP/2-Spezifikation definiert ist, ermöglicht es einem Server, Ressourcen präventiv an einen Remote-Client zu pushen, in der Erwartung, dass der Client diese Ressourcen bald anfordern könnte. Auf diese Weise können Sie möglicherweise die Anzahl der RTTs (Round Trip Time – die für eine Anfrage und Antwort benötigte Zeit) bei einem Seitenladevorgang um eine RTT oder mehr reduzieren und so dem Benutzer eine schnellere Antwort bieten.

Mithilfe von Server-Push kann ein Client mit Stylesheets, Bildern und anderen Ressourcen versorgt werden, die er zum Rendern einer Webseite benötigt. Sie sollten darauf achten, nur die Ressourcen zu pushen, die erforderlich sind. Pushen Sie keine Ressourcen, die ein Client wahrscheinlich bereits zwischengespeichert hat.

In diesem Blogbeitrag beschreibe ich:

Konfigurieren von HTTP/2 Server Push

Um beim Laden einer Seite Ressourcen zu pushen, verwenden Sie die Direktive http2_push wie folgt:

server { # Stellen Sie sicher, dass HTTP/2 für den Server aktiviert ist. Listen 443 ssl http2 ; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # wenn ein Client demo.html anfordert, pushen Sie auch # /style.css, /image1.jpg und /image2.jpg location = /demo.html { http2_push /style.css; http2_push /image1.jpg; http2_push /image2.jpg; } }

Überprüfen des HTTP/2-Server-Push

Mit einer der beiden Methoden können Sie ganz einfach überprüfen, ob der Server-Push aktiv ist:

  • Die Entwicklertools in Ihrem Webbrowser
  • Ein HTTP/2‑Befehlszeilenclient wie nghttp

Überprüfen mit Entwicklertools (Google Chrome)

So verwenden Sie die Entwicklertools in Ihrem Webbrowser, um am Beispiel von Google Chrome zu überprüfen, ob der Server-Push aktiviert ist. In der Abbildung zeigt die Spalte „Initiator“ auf der Registerkarte „ Netzwerk “ der Entwicklertools von Chrome an, dass im Rahmen einer Anforderung für /demo.html mehrere Ressourcen an den Client gesendet wurden.

Die Spalte „Initiator“ gibt an, dass Server-Push zum Senden von Ressourcen verwendet wurde.

Überprüfen mit einem Befehlszeilenclient ( nghttp )

Zusätzlich zu den Webbrowser-Tools können Sie den nghttp- Befehlszeilenclient aus dem nghttp2.org -Projekt verwenden, um zu überprüfen, ob Server-Push aktiv ist. Sie können den nghttp -Befehlszeilenclient von GitHub herunterladen oder das entsprechende Betriebssystempaket installieren, sofern verfügbar. Verwenden Sie für Ubuntu das Paket nghttp2-client .

In der Ausgabe markiert das Sternchen (*) Ressourcen, die vom Server gepusht wurden.

$ nghttp -ans https://example.com/demo.html ID AntwortEnde AnfrageStart Prozesscode Größe Anfragepfad 13 +84,25 ms +136us 84,11 ms 200 492 /demo.html 2 +84,33 ms * +84,09 ms 246us 200 266 /style.css 4 +261,94 ms * +84,12 ms 177,83 ms 200 40 K /image2.jpg 6 +685,95 ms * +84,12 ms 601,82 ms 200 173 K /image1.jpg

Automatisches Übertragen von Ressourcen an Clients

In vielen Situationen ist es unpraktisch – oder sogar unmöglich –, die Ressourcen, die Sie pushen möchten, in der NGINX-Konfigurationsdatei aufzulisten. Aus diesem Grund unterstützt NGINX auch die Konvention, Link- Preload-Header abzufangen und dann die in diesen Headern identifizierten Ressourcen zu pushen. Um das Vorladen zu aktivieren, nehmen Sie die Direktive http2_push_preload in die Konfiguration auf:

server { # Stellen Sie sicher, dass HTTP/2 für den Server aktiviert ist. listen 443 ssl http2 ; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # Link-Header abfangen und angeforderte Pushes initiieren location = /myapp { proxy_pass http://upstream; http2_push_preload on; } }

Wenn NGINX beispielsweise als Proxy fungiert (für HTTP, FastCGI oder andere Datenverkehrstypen), kann der Upstream-Server seiner Antwort einen Link -Header wie diesen hinzufügen:

Verknüpfung: </style.css>; als=Stil; rel=preload

NGINX fängt diesen Header ab und leitet einen Server-Push von /style.css ein. Der Pfad im Link -Header muss absolut sein – relative Pfade wie ./style.css werden nicht unterstützt. Der Pfad kann optional eine Abfragezeichenfolge enthalten.

Um mehrere Objekte zu pushen, können Sie mehrere Link -Header angeben oder, noch besser, alle Objekte in eine durch Kommas getrennte Liste aufnehmen:

Verknüpfung: </style.css>; als=Stil; rel=preload, </favicon.ico>; als=Bild; rel=preload

Wenn Sie nicht möchten, dass NGINX eine vorinstallierte Ressource pusht, fügen Sie dem Header den Parameter „nopush“ hinzu:

# Ressource wird nicht gepushtLink: </nginx.png>; as=image; rel=preload; nopush

Wenn http2_push_preload aktiviert ist, können Sie das Preload-Server-Push auch initiieren, indem Sie den Antwortheader in Ihrer NGINX-Konfiguration festlegen:

add_header Link "</style.css>; als=Stil; rel=preload";

Selektives Weiterleiten von Ressourcen an Clients

Die HTTP/2-Spezifikation befasst sich nicht mit der Herausforderung, zu bestimmen, ob Ressourcen gepusht werden sollen oder nicht. Natürlich empfiehlt es sich, Ressourcen nur dann an Clients weiterzuleiten, wenn Sie wissen, dass diese die Ressource wahrscheinlich benötigen und dass sie diese wahrscheinlich nicht bereits zwischengespeichert haben.

Ein möglicher Ansatz besteht darin, den Clients Ressourcen nur bei ihrem ersten Besuch auf der Site bereitzustellen. Sie können beispielsweise testen, ob ein Sitzungscookie vorhanden ist, und den Link -Header bedingt festlegen, sodass die Ressourcen nur dann vorab geladen werden, wenn das Sitzungscookie nicht vorhanden ist.

Vorausgesetzt, die Clients verhalten sich gut und schließen das Cookie in nachfolgende Anfragen ein, überträgt NGINX mit der folgenden Konfiguration die Ressourcen nur einmal pro Browsersitzung an die Clients:

Server {
Listen 443 SSL http2 Standardserver;

SSL-Zertifikat ssl/Zertifikat.pem;
SSL-Zertifikatschlüssel ssl/Schlüssel.pem;

Root /var/www/html;
http2_push_preload ein;

Standort = /demo.html {
Add_header Set-Cookie "Sitzung = 1";
Add_header Link $resources;
}
}

Map $http_cookie $resources {
"~*Sitzung = 1" "";
Standard "</style.css>; as=style; rel=preload, </image1.jpg>; as=Bild; rel=preload, </image2.jpg>; as=Bild; rel=preload";
}

Messen der Wirkung von HTTP/2-Server-Push

Um die Wirkung des Server-Pushs zu messen, haben wir eine einfache Testseite, /demo.html , erstellt, die auf ein separates Stylesheet, /style.css , verweist. Das Stylesheet verweist außerdem auf zwei Bilder. Wir haben die Seitenladezeiten mit drei verschiedenen Konfigurationen getestet:

  • Sequentielle GET s (Keine Optimierung) – Der Browser hat Ressourcen geladen, als er erkannte, dass diese benötigt wurden
  • Preload-Hinweise – Preload-Hinweise ( Link -Header) wurden in die erste Antwort aufgenommen, um den Browser anzuweisen, die Abhängigkeiten zu laden
  • Server Push (nur HTTP/2) – Abhängigkeiten wurden präventiv an den Browser gesendet
Drei Konfigurationen wurden getestet, um die Auswirkungen von HTTP/2 mit Server-Push zu messen

Wir haben mehrere Testläufe jeder Konfiguration mit HTTP, HTTPS oder HTTP/2 durchgeführt. Die ersten beiden Konfigurationen gelten für alle drei Protokolle und Server-Push nur für HTTP/2.

Das Verhalten wurde mit den Chrome-Entwicklertools gemessen. Das am häufigsten vorkommende Verhalten jeder Konfiguration wurde bewertet und gemittelt, und die Zeiten wurden mit der RTT der Verbindung (gemessen mit Ping ) korreliert, um die mechanische Wirkung jeder Methode zu veranschaulichen.

Testergebnisse zeigen, dass bei jeder Konfiguration viele Hin- und Rückfahrten erforderlich waren

Einige grundlegende Beobachtungen

  • „DOM geladen“ ist die Zeit, die zum Herstellen einer neuen Verbindung und zum Abrufen der Seite „demo.html“ benötigt wird. Stylesheet ist die Zeit zum Abrufen der CSS-Ressource.
  • Das Herstellen einer Verbindung über HTTP dauert 1 RTT, für HTTPS und HTTP/2 dauert es 2 RTTs.
  • Die Nutzlast für die HTML- und CSS-Ressourcen ist kleiner als die Größe der maximalen Übertragungseinheit (MTU), sodass ein GET- Vorgang in etwa 1 RTT abgeschlossen ist.

Interpretation der Ergebnisse: Hinweise zum Vorladen

  • Preload-Hinweise haben nur minimale Auswirkungen auf die CSS-Ressource, da innerhalb der HTML-Ressource direkt darauf verwiesen wird und die HTML-Ressource schnell bereitgestellt wird. Der Browser initiiert die CSS-Anfrage, sobald die HTML-Seite ausgeliefert wird.
  • Preload-Hinweise bewirken, dass der Download von Ressourcen, die in der CSS-Ressource deklariert sind (hier die beiden Bilder), schnell gestartet wird. Wenn Preload-Hinweise verwendet werden, können die Downloads 1 RTT schneller starten.
  • Die Nettoeinsparungen durch Vorladehinweise können 1 RTT oder mehr betragen. Beim parallelen Herunterladen von Ressourcen muss der Browser eine oder mehrere zusätzliche Verbindungen öffnen. Die Leistung hängt davon ab, ob langsame Anforderungen (größere Antworten) zuerst geplant oder verzögert werden, während neue Verbindungen geöffnet werden. Diese unvorhersehbare Anforderungsreihenfolge ist für die 1-RTT-Beschleunigung für HTTP und HTTP/2 und die 2-RTT-Beschleunigung für HTTPS verantwortlich.

Interpretation der Ergebnisse: Server-Push

  • Durch Server-Push wurde die Zeit zum Vorladen der Hinweise um weitere 1 RTT verbessert. Die Push-„Antworten“ wurden gleichzeitig mit der Antwort auf die erste Anfrage initiiert, während bei den Antworten mit den Preload-Hinweisen eine Verzögerung von 1 RTT auftrat – 0,5 RTT für die Antwort auf die erste Anfrage plus 0,5 RTT für die Preload -GET -Anfrage.

Testnotizen

  • Für jede Konfiguration gab es mehrere Testläufe. Jeder Lauf begann mit einem leeren Browser-Cache und ohne hergestellte Keepalive-Verbindungen zum NGINX-Server. Die NGINX-Direktiven keepalive_timeout und http2_idle_timeout wurden verwendet, um Keepalive-Verbindungen schnell zu schließen.
  • Derzeit scheint es nicht möglich zu sein, Schriftartressourcen zu Chrome zu übertragen, möglicherweise aufgrund einer bekannten Komplikation . Chrome fordert eine Schriftartressource explizit an, auch wenn diese bereits gesendet wurde.
  • Es wurde darauf geachtet, die Browser-Caches vor jedem Test explizit zu löschen und alle Inhalte wurden mit abgelaufenen Cache-Control-Headern bereitgestellt.
  • Chrome speichert vorinstallierte Ressourcen im Cache. Diese zwischengespeicherten Ressourcen werden nicht immer ignoriert, wenn Sie die Zwischenspeicherung deaktivieren, und werden nicht immer durch einen expliziten Vorgang zum Löschen des Browser-Cache gelöscht. (Eine Möglichkeit zum Deaktivieren der Zwischenspeicherung in Chrome besteht darin, das Kontrollkästchen „Cache deaktivieren“ auf der Registerkarte „Entwicklertools -Netzwerk“ zu aktivieren. Um den Browser-Cache zu leeren, können Sie bei geöffneten Entwicklertools mit der rechten Maustaste auf die Schaltfläche zum Aktualisieren des Browsers klicken und „Cache leeren und neu laden “ auswählen.
  • Bei einigen Versuchen, Inhalte vorab zu laden, konnte Chrome die zwischengespeicherten Kopien nicht erfolgreich erneut validieren und die Ressource anschließend normal herunterladen. Diese Versuche wurden bei der Messung nicht mitgezählt.
  • Chrome fügt allen neuen SSL-Verbindungen, die zuvor akzeptierte selbstsignierte Zertifikate verwenden, eine unnötige Verzögerung von 2 RTT hinzu. Der Test wurde mit von einer Zertifizierungsstelle signierten Let’s Encrypt-Zertifikaten durchgeführt, um diese 2-RTT-Verzögerung zu vermeiden.
  • DNS-Verzögerungen wurden durch Bearbeiten der lokalen Datei /etc/hosts behoben.
  • Bei diesen einfachen Tests wurde nicht versucht, die Wirkung von Server-Push zu messen, wenn der Client bereits über eine zwischengespeicherte Kopie der Ressource verfügt. Bei den Tests wurden vor jedem Test sämtliche Caches geleert und die meisten Pushes erfolgten zu schnell, um sie abzubrechen.

Abschluss

Dieser Test war bewusst einfach gehalten, um die Funktionsweise der Vorladehinweise und des Server-Pushs hervorzuheben. Server-Push liefert in einfachen Situationen eine 1-RTT-Verbesserung gegenüber Preload-Hinweisen und eine größere Verbesserung im Vergleich zu nicht optimierten, sequenziellen GET- Anfragen und der Erkennung abhängiger Ressourcen.

In realistischeren Anwendungsfällen gibt es wesentlich mehr Variablen: mehrere abhängige Ressourcen, mehrere Quellen, sogar die Möglichkeit einer Bandbreitenverschwendung durch das Pushen von Ressourcen, die bereits zwischengespeichert sind oder nicht unmittelbar benötigt werden. Auch Browserinkonsistenzen wirken sich auf die Leistung aus. Ihr Kilometerstand wird bei diesem einfachen Test sicherlich abweichen.

Das Chrome-Team hat beispielsweise einige ausführliche Empfehlungen dazu veröffentlicht, wann Server-Push bereitgestellt werden sollte, und hat auf komplexeren Sites Messungen durchgeführt, um die Auswirkungen von fehlender Optimierung, Vorladehinweisen und Server-Push über HTTP/2 zu vergleichen. Ihr Bericht „Faustregeln für HTTP/2-Push“ ist für jeden lesenswert, der den Einsatz von HTTP/2-Server-Push in der Produktion erwägt.

Die pragmatische Schlussfolgerung lautet: Wenn Sie im Voraus ermitteln können, welche Ressourcen erforderlich sind, ist es von echtem Nutzen, wenn Upstream-Server einen Vorladehinweis senden. Der zusätzliche Nutzen durch die Nutzung dieser Ressourcen ist gering, aber messbar, kann aber möglicherweise zu einer Verschwendung von Bandbreite und Verzögerungen bei benötigten Ressourcen führen. Sie sollten alle Server-Push-Konfigurationen sorgfältig testen und überwachen.

Anhang: Wie funktioniert HTTP/2-Push?

Die folgenden Informationen basieren teilweise auf den Untersuchungen in Jake Archibalds sehr detailliertem Blogbeitrag „HTTP/2-Push ist schwieriger als ich dachte“ .

HTTP/2-Server-Push wird normalerweise verwendet, um abhängige Ressourcen präventiv zu senden, wenn der Client eine Ressource anfordert. Wenn ein Client beispielsweise eine Webseite anfordert, kann der Server abhängige Stylesheets, Schriftarten und Bilder an den Client senden.

Wenn ein Client eine HTTP/2-Verbindung herstellt, kann der Server eine oder mehrere Server-Push-Antworten über die Verbindung initiieren. Diese Pushes senden Ressourcen, die der Client nicht explizit angefordert hat.

Der Client kann einen Push entweder ablehnen (durch Senden eines RST_STREAM- Frames) oder akzeptieren. Der Client speichert den gepushten Inhalt in einem lokalen „Push-Cache“, der mit der HTTP/2-Verbindung verknüpft ist.

Wenn der Client später über eine bestehende HTTP/2-Verbindung eine Anforderung für eine Ressource stellt, prüft er den Push-Cache der Verbindung auf eine abgeschlossene oder in Übertragung befindliche Antwort auf die Anforderung. Dabei wird die zwischengespeicherte Ressource bevorzugt verwendet, anstatt eine neue HTTP/2-Anfrage für die Ressource zu stellen.

Jede gepushte Ressource verbleibt im Push-Cache pro Verbindung, bis (a) sie verwendet wird oder (b) die HTTP/2-Verbindung geschlossen wird:

  1. Wird die Ressource verwendet, erstellt der Client eine Kopie und der Eintrag im Push-Cache wird gelöscht. Wenn die Ressource zwischengespeichert werden kann, kann der Client ihre Kopie in seinem HTTP-Seitencache zwischenspeichern.
  2. Wenn die HTTP/2-Verbindung aus irgendeinem Grund geschlossen wird, wird ihr lokaler Push-Cache gelöscht.

Dies hat mehrere Auswirkungen:

  • Inhalte im HTTP-Seitencache des Browsers werden gegenüber Inhalten im Push-Cache bevorzugt verwendet, auch wenn die gepushten Inhalte aktueller sind.
  • HTTP/2-Verbindungen können für verschiedene Seitenladevorgänge gemeinsam genutzt werden. Eine Ressource, die als Ergebnis des Ladens einer Seite gepusht wird, kann bei Anforderung beim Laden einer anderen Seite verwendet werden.
  • Anfragen mit Anmeldeinformationen verwenden andere HTTP/2-Verbindungen als Anfragen ohne Anmeldeinformationen. Beispielsweise wird eine Ressource, die mit einer Cross-Origin-Anfrage (mit Anmeldeinformationen) gepusht wird, möglicherweise nicht gefunden, wenn der Browser eine Anfrage für die Ressource ohne Anmeldeinformationen stellt.

Eine viel detailliertere Liste der Probleme finden Sie in Jake Archibalds Blogbeitrag „HTTP/2-Push ist schwieriger als ich dachte“ .

HTTP/2-Server-Push ist eine interessante Funktion. Stellen Sie sicher, dass Sie die Push-Konfiguration Ihres HTTP/2-Servers gründlich testen und bereit sind, in Fällen auf Preload-Hinweise zurückzugreifen, in denen dies zu einem vorhersehbareren, Cache-bewussteren Verhalten führt.


„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."