Http/2 and Push - some problems with browsers - Umstellung von Server-Daten auf http/2 - an zu vielen Pushs scheinen sich FireFox und Chrome zu verschlucken

06.10.2019 23:53:56, Jürgen Auer, keine Kommentare

Kürzlich war es mal wieder so weit: Server-Daten bekam neue Server, die wurden eingerichtet. Am letzten Wochenende wurde umgezogen.

Damit der Wechsel von Windows 2012 R2 auf die aktuelle Version Windows 2019 Server.

Und das führte zu der Möglichkeit, Http/2 nutzen zu können.

Http/2 ist das Nachfolgeprotokoll von Http 1.1. Das stammte von 1999 und bildete seither eine technische Basis für alle Websites. Es hatte aber ein paar grundsätzliche Einschränkungen. Die waren 1999 noch ok. Führten aber in der Folgezeit immer wieder zu Problemen bzw. zu "merkwürdigen Workarounds".

Http/2 ist deshalb einerseits vollständig abwärtskompatibel. Heißt: Man muß überhaupt nichts ändern und kann Http/2 trotzdem sofort nutzen.

Es ist aber anders aufgebaut und beschleunigt damit bestehende Anwendungen sofort.

Ein Prinzip bei Http.1.1: Pro Anforderung wird eine neue Tcp-Verbindung benötigt. Heißt: Der Browser öffnet eine Tcp-Verbindung. Dann wird darüber eine SSL-Verbindung hergestellt. Dann sagt der Browser, was er haben möchte:

> GET /de/ HTTP/1.1
> Host: blog.server-daten.de

und der Server schickt das ganze Dokument (hier: Die Startseite des Blogs) zurück. Der Browser parst das, stößt auf CSS- und JavaScript-Dateien sowie auf Bilder. Und öffnet für jedes dieser Objekte ... eine neue Tcp-Verbindung, über die eine SSL-Verbindung hergestellt wird, über welche die Ressource abgerufen wird.

Das zentrale Problem: Jedes Herstellen dieser Kombination aus Tcp-Verbindung und SSL-Verbindung benötigt ein paar Roundtrips, eine Art Pingpong-Spiel zwischen Browser und Server. Das dauert zwangsläufig.

Http/2 räumt damit radikal auf, indem nur noch eine Tcp-Verbindung hergestellt wird, darüber eine Verschlüsselung - und über diese wird alles abgewickelt.

Wobei man sich eine bestehende Tcp-Verbindung nicht wie eine Telefonverbindung vorstellen darf, bei der immer nur einer reden kann. Eher ist das wie der Austausch von Briefkastenadressen: "Das ist mein Briefkasten für Dich, da liefere bitte die Daten ein". Es kann also parallel viel gesendet und viel empfangen werden.

Ein zweites Feature sind "komprimierte Header". Das nimmt man mit, ohne daß man dazu viel machen könnte.

Ein drittes, neues und verwendbares Feature sind "Server Pushs". Die Idee: Wenn ein Browser eine Html-Seite abruft, dann kann es sein, daß der Server weiß: "Jetzt werden auch eine JavaScript- und eine CSS-Datei benötigt". Weil beide Dateien in der Html-Seite als weitere Ressourcen eingebunden sind.

Also liegt es nahe, daß der Webserver dem Browser "vorschlägt", er möge sich diese Dateien auch gleich herunterladen.

Das ließ sich auch schnell einbauen: Weil so gut wie jede Seite innerhalb von Server-Daten eine Standard-JS-Datei nutzt, kann diese per Push vorgeschlagen werden. Weil ferner zur Subdomain blog immer eine CSS-Datei /css/blog.css existiert, die aber über einen Aufruf der Form /css/blog.20191006220025.css erfolgt, kann auch die letztere Datei als Push vorgeschlagen werden.

Das eingebaut, das funktionierte soweit. Bei ausgeschaltetem lokalen Cache zeigt FireFox bei diesen beiden Dateien merkwürdig leere Einträge an: Kein Http-Status, kein Typ, keine Größe. Keine Kopfzeilen, kein Waterfall.

Dann stolperte ich über diesen Beitrag:

HTTP/2: the difference between HTTP/1.1, benefits and how to use it

https://medium.com/@factoryhr/http-2-the-difference-between-http-1-1-benefits-and-how-to-use-it-38094fa0e95b

Eine Testseite mit 100 kleinen Bildern. Bei Http.1.1 sieht man deutlich die "sequentielle Verarbeitung". 12 Sekunden insgesamt. Bei Http/2 ohne Push: Ebenfalls knapp 12 Sekunden Gesamtladezeit. Etwas wunderte mich das. Ich hatte mit einer schnelleren Verarbeitung gerechnet.

Bei http/2 mit Push dagegen: Etwa 3 Sekunden Gesamtladezeit. Das Dokument ist geladen, dann folgen quasi alle Bilder parallel.

Allerdings war das schnell ernüchternd: Beim hiesigen Blog einen Push für alle eingebundenen (knapp 40) Bilder eingebaut, der zu einem Zeitpunkt ausgeführt wird, zu dem die Seite noch gar nicht an den Client geschickt wurde. Sowohl FireFox als auch Chrome "gurkten rum". Den Code ans Ende verschoben, in den Render-Abschnitt, aber nach der Ausführung der zentralen Xsl-Transformation: FireFox war stabiler, Chrome gurkte erneut. Bei FireFox funktionierte das stabil, wenn die Netzwerkkonsole zu war. Bei offener Netzwerkkonsole führte das eher zu einem Aufhänger.

Es scheint, daß zu viele bzw. zu große Pakete entweder auf dem Server oder beim Browser zu einem "Verschlucken" führen. Eventuell muß man eine Art "Arbeitsthread" starten, nachdem die eigentliche Seite komplett ausgeliefert wurde. Dieser Arbeitsthread schickt anschließend die Push-Nachrichten ab.

Ferner scheint Chrome eine Art interne Drosselung auf maximal 10 Dateien zu nutzen.

Sprich: Die Frage, an welcher Stelle im Seitencode die Pushs eingebaut werden, ist für eine funktionierende Lösung entscheidend. Mal sehen, ob sich das in den nächsten Wochen noch genauer herausfinden läßt.

Weitaus wichtiger (und innerhalb von Server-Daten schon vor ein paar Jahren implementiert) sind drei andere Dinge:

Zum einen, daß eingebundene Ressourcen mit einem langen Expires-Header ausgeliefert werden (ein halbes Jahr). So daß der Browser diese Ressourcen cacht. Ein Bild oder eine JS-Datei mit "Expires: 0" auszuliefern ist Unsinn.

Zum zweiten wird eine Logik benötigt, die bei Änderungen in CSS- und JS-Dateien den Dateinamen ändert. So daß man trotz des Cachings Daten ändern kann.

Und zum dritten die korrekte Auslieferung mit GZip (CSS-, JS und unkomprimierte Bildformate).

Alle drei Punkte sind Vorarbeiten, die relevanter sind als ein Server-Push. Denn wenn die Daten korrekt gecacht werden und der Browser weiß, daß sich diese nicht ändern, dann werden die Dateien gar nicht abgerufen.

---

Kleines Update, einen Tag später: Es stellte sich heraus, daß man die zusätzlichen Bilder-Pushs nicht im Render-Abschnitt, sondern erst im Unload-Teil unterbringen sollte. Macht man das im Render-Block, scheint sich der Browser erst um diese Push-Urls zu kümmern, der Server schließt die Seite erst später ab. Macht man das im Unload-Block, dann ist der Seitencode komplett verarbeitet und zum Client geschickt worden. Dort kann man die obersten 5 Bilder per Push absenden (plus die Standard-JS und die CSS-Datei). Wenn die Zahl der Pushs nicht zu hoch ist, wird das stabil von beiden Browsern verarbeitet. Alle 40 Bilder zu pushen führte erneut zu Hängern. 5 Pushs funktionierten.

*
* (wird nicht angezeigt)
Die Erläuterungen zum Datenschutz habe ich gelesen und stimme diesen zu.