Teil 10: Latenz

Qualität von Webanwendungen: Das Ding mit der Latenz

Daniel Takai, Nicolas Bär, Christian Wittwer

© iStockphoto.com/PeterPolak

Die Latenz ist ein häufig unterschätzter Faktor der Performance von Websystemen. Lokal bei der Entwicklung nicht bemerkbar, kumulieren sich die Laufzeiten über die Distanz. Insbesondere bei Websystemen, bei denen die globale Verfügbarkeit oft als Vorteil ins Feld geführt wird, können so lange Laufzeiten entstehen. Wir erläutern die Hintergründe und zeigen Methoden zur Verbesserung auf.

Die Latenz wird durch die räumliche Distanz, das verwendete Protokoll und das Antwortverhalten unseres Websystems beeinflusst. Üblicherweise misst man sie in Millisekunden und unterscheidet zwischen One-Way- und Round-Trip-Messungen. Letztere sind einfacher durchzuführen, da man sich nicht um die Synchronisation der Uhren zwischen den beiden Messpunkten kümmern muss. Zudem enthält die Roundtrip-Messung auch die Antwortzeit unseres Websystems, sodass wir eine Zahl erhalten, die dem entspricht, was der entfernte Benutzer auf seinem Client erlebt. Zwei wesentliche Variablen spielen hier mit: die Kapazität unseres Systems sowie die individuelle Perspektive des Benutzers aufgrund der räumlichen Verteilung. Abbildung 1 zeigt die Konzepte rund um die Latenz, die wir in diesem Artikel besprechen.

Man muß versuchen, bis zum Äußersten ins Innere zu gehen, denn der Feind des Menschen ist die Oberfläche“ —Samuell Beckett

Abb. 1: Konzepte rund um die Latenz

Abb. 1: Konzepte rund um die Latenz

Entscheidende Protokollfragen

Die Latenz von Kommunikationswegen wird maßgeblich vom Protokoll beeinflusst. Dieses definiert die Regeln für den Austausch von Informationen innerhalb eines Netzwerks. Ein Web Service verwendet verschiedene Protokolle, um mit den Umsystemen zu kommunizieren. Beim Aufruf eines URLs wird immer DNS verwendet, um die Adresse aufzulösen und HTTP oder HTTPS für die Kommunikation zwischen Browser und Web Service. Falls der Web Service mit einer Datenbank kommuniziert, wird dabei wieder ein spezifisches Protokoll verwendet (z. B. das MySQL-Protokoll). Um den Einfluss von Protokollen auf die Performance zu verstehen, diskutieren wir zunächst den Unterschied von HTTP zu HTTP/2 und besprechen folgend das Vorgehen zur Messung von Latenz über verschiedene Protokolle.

Die erste Version des HTTP-Protokolls wurde 1997 publiziert, 2015 folgte HTTP/2, eine optimierte und verbesserte Version des Protokolls. Sie beinhaltet viele performancerelevante Verbesserungen basierend auf den Erfahrungen aus dem SPDY-Protokoll (Chromium-Projekt). In HTTP/1.1 muss der Browser jeweils eine initiale Abfrage senden, um das HTML-Dokument zu erhalten. Danach werden sukzessive Anfragen für Ressourcen (z. B. CSS, JS und Bilder) einzeln gestartet. Ein Browser startet heute mehrere TCP-Verbindungen (vier bis acht) zu einem Webserver und verteilt die Anfragen auf die geöffneten Verbindungen. Dabei kommt es zu dem Head-of-line-Blocking-Problem, bei dem sich nur eine Anfrage pro Verbindung verarbeiten lässt. Entsprechend kann diese Verbindung den Fluss blockieren. Ebenfalls muss für die Verbindungen jeweils ein Handshake durchgeführt werden. Abbildung 2 zeigt ein entsprechendes Wasserfalldiagramm.

Abb. 2: Wasserfalldiagramm

Abb. 2: Wasserfalldiagramm

HTTP/2 optimiert die Anzahl der Verbindungen und den Handshake Overhead, indem eine TCP-Verbindung pro Host für mehrere Anfragen und Antworten verwendet werden kann (Multiplexing). Das Protokoll erlaubt, dass mehrere Nachrichten miteinander vermischt versendet werden können. Eine einzelne TCP-Verbindung reduziert nicht nur die Anzahl der Handshakes, sondern bringt auch einen weiteren wichtigen Vorteil: Eine TCP-Verbindung ermittelt über den so genannten Slow Start eigenständig die Übertragungsrate. Beim Slow Start beginnt die Übertragungsrate auf einem niedrigen Niveau und wird fortlaufend erhöht. Mit Multiplexing muss dieser Slow Start nur einmal ausgeführt werden.

Damit der Wasserfalleffekt reduziert werden kann, muss der Browser Heuristiken anwenden, die versuchen, die Abfragen der Ressourcen zu verteilen. In HTTP/2 können Ressourcen priorisiert werden, und der Browser kennzeichnet Abhängigkeiten zwischen Anfragen, um die Bandbreite maximal auszunutzen. Zum Beispiel ist es sinnvoll, dass zuerst CSS-Dateien geladen werden, damit der Browser bereits mit dem Rendering starten kann, bevor die Bilder einer Seite heruntergeladen werden.

Das HTTP-Protokoll beinhaltet einen ausführlichen Header, der jeweils über verschiedene Anfragen und Antworten ähnliche Felder beinhaltet. Zum Beispiel wird bei jeder Anfrage der User-Agent (Browserinformation) im Header festgehalten, oder es werden Cookies übermittelt. Das HTTP-Protokoll ist zustandslos implementiert, und es werden keine Informationen zur Kommunikation gespeichert. Mit HTTP/2 wird zumindest während einer Verbindung der Header zwischengespeichert, und somit lassen sich die redundanten Felder komprimieren.

Eine weitere Änderung in HTTP/2 ist das Push-Verfahren. Dabei kann der Server Ressourcen an den Client senden, die er noch nicht angefragt hat. Wie oben beschrieben, muss der Browser initial ein HTML herunterladen, dieses analysieren und dann die referenzierten Ressourcen abfragen. Der Server kann nun bereits die Ressourcen an den Browser senden, obwohl dieser noch das HTML analysiert und nicht weiß, dass die Ressourcen benötigt werden.

Die Performanceeigenschaften eines Protokolls zu verstehen, ist also wichtig, wie das Beispiel von HTTP/2 zeigt. Spannend erweisen sich diese Optimierungen vor allem in der Kommunikation mit mobilen Endgeräten, wo die Netzwerklatenz stark variiert und entsprechend minimiert wird. Es ist jedoch wichtig, sich bei der Performanceanalyse einer Applikation nicht nur auf das Protokoll zu fokussieren, sondern auch auf weitere klare Indizien, die auf Netzwerklatenz hindeuten.

HTTP ist ein spezieller Fall, da wir keinen Einfluss auf die Infrastruktur unserer Benutzer nehmen können. Wenn wir beispielsweise Latenzprobleme zwischen unserem Web Service und der MySQL-Datenbank erkennen, können wir die Latenz reduzieren, indem wir die physikalische Distanz verkürzen. Natürlich bietet das MySQL-Protokoll noch weitere Optionen, unter anderem die Komprimierung. Diese kann unsere Payload auf Kosten der CPU-Auslastung verkleinern. Diese Option ist nützlich, wenn sehr große Result-Sets von MySQL ausgeliefert werden.

Per Latenzdiagramm analysieren

Die geografische Verteilung hat den größten Einfluss auf die Latenz. Es kann aber auch sein, dass im lokalen System nicht nur der Administrator eine lange Leitung hat. Es sind immer wieder die eingebundenen externe Dienste, die uns unsere Performancesuppe versalzen: mit langsamen Antwortzeiten, also langen Roundtrips. Um diese Probleme früh zu erkennen, lohnt sich ein Latenzdiagramm. Abbildung 3 zeigt ein solches Diagramm, in dem die beteiligten Bausteine, Protokolle und deren Latenzen abgebildet sind. Daraus lässt sich einfach die gesamte Latenz eines Zugriffs mittels Addition ableiten. Im Diagramm sind jeweils das Protokoll und die Latenz in Millisekunden angegeben.

Das Latenzdiagramm kann aus der Bausteinsicht unseres Websystems erstellt werden. Im Beispiel sind ein ESB und ein PIM eingezeichnet, die auf einer E-Commerce-Website ihre Dienste tun. Die Latenzen zum Zugriff wurden per Curl gemessen, die Verbindung zur MongoDB per Socket durch etwas handgestrickten PHP-Code im Akeneo. Bei der Messung per Curl wird ein statisches Asset abgerufen, damit die Geschwindigkeit der Webanwendung nicht das Ergebnis verschmutzt.

Abb. 3: Latenzdiagramm: Protokoll und Latenz in Millisekunden

Abb. 3: Latenzdiagramm: Protokoll und Latenz in Millisekunden

Ein solches Diagramm wie in Abbildung 3 zeigt eine stark vereinfachte, da statische Sicht der Dinge. Im Produktivbetrieb können externe Dienste, aber auch die eigene Infrastruktur, ihr Verhalten ändern. Dennoch erhöhen regelmäßige Messungen die Zuverlässigkeit unserer Architektur. So haben wir bereits Dienste aus dem synchronen Zugriff genommen, da sonst die Latenz zu groß gewesen wäre, denn das hätte rückläufige Besucherzahlen bedeutet. Wenn ein externer Dienst bis zu einer Minute benötigt, um eine Anfrage zu bearbeiten, dann ist man als Architekt hilflos und muss einen Workaround finden.

Innerhalb eines Rechenzentrums kann auch die physische Distanz eine Rolle spielen, denn Latenzen summieren sich. Wenn ein Seitenaufbau beispielsweise einige tausend DB-Abfragen benötigt, so potenziert sich jede Millisekunde zusätzliche Latenz zu einer spürbaren Wartezeit. In einem solchen Fall haben sie aber noch ganz andere Probleme, denn sie sollten zunächst mit dem Entwicklerteam sprechen, das die Seite gebaut hat.

Nähe durch Content Delivery Network

Die geografische Nähe zum Benutzer ist ein wesentlicher Faktor für die Geschwindigkeit einer Webapplikation. Die durch Distanz eintretende Verzögerung der Netzwerkverbindung lässt sich dadurch optimieren, dass sich die Anzahl benötigter Roundtrips zur Webapplikation verringert. Die eigentliche Latenz lässt sich aber nicht verbessern – denn die Lichtgeschwindigkeit ist eine physikalische Grenze.

Die Webapplikation näher an den Benutzer zu bringen, ist der einzige Weg für Verbesserung. Ein erster Schritt kann ein Reverse Proxy in Asien sein, der Inhalte zwischenspeichert. Ein virtueller Server lässt sich heute in vielen Regionen der Welt dank Cloud Computing einfach und schnell mieten. Bei steigendem Traffic oder der Expansion in andere Länder muss die immer komplexer werdende Infrastruktur kontinuierlich angepasst werden, was in den meisten Fällen weder aus wirtschaftlicher noch betrieblicher Sicht Sinn ergibt.

Ein Content Delivery Network (CDN), wie in Abbildung 4 dargestellt, ist im Prinzip genau das: ein weltweit verteilter Verbund aus Reverse-Proxy-Servern. Diese Reverse-Proxy-Server werden im Context von CDN häufig als Points of Presence (POP) bezeichnet, unser Websystem ist dabei der so genannte Origin. Akamai nennt die POPs auch Edge-Server.

Abb. 4: Funktionsweise eines CDN

Abb. 4: Funktionsweise eines CDN

Der CDN-Provider sorgt hierbei dafür, dass ein Benutzer immer über den geografisch nächsten POP geroutet wird. Der POP prüft, ob die angeforderte Ressource im Cache vorhanden ist, lädt diese bei Bedarf vom Origin und liefert diese schlussendlich aus. Der Origin kann das Verhalten des CDN über die Verwendung der passenden HTTP-Header für Caching und Time to Live (TTL) steuern. Das ist handelsübliche Technik, jedoch muss sich die Entwicklung genaue Gedanken über die richtigen Cache-Header machen, damit keine veralteten Ressourcen beim Benutzer landen.

Damit die Ressourcen über das CDN ausgeliefert werden, müssen diese richtig verlinkt werden. Im unserem Beispiel ist unser Origin unter dem URL http://www.foo.com erreichbar. Der CDN-Provider stellt uns einen speziellen URL http://foo.cdn.com zur Verfügung, der auf das CDN zeigt. Die Webapplikation wird anschließend entsprechend konfiguriert, sodass alle statischen Assets über diesen URL verlinkt werden. Diese Requests landen damit automatisch auf dem CDN und werden gecacht.

Die Auslieferung von statischen Assets via CDN ist der klassische Anwendungsfall. Der initiale Request wird vom Origin beantwortet, die restlichen Requests für JavaScript, Bilder und CSS kommen vom CDN. Mit diesem Verfahren lässt sich eine Webapplikation global in vielen Fällen mit einer angemessenen Geschwindigkeit ausliefern. Zudem lassen sich große Datenmengen (Dowloads, Videos, Bilder etc.) effizient verteilen, da ein CDN durch das Caching auch automatisch den Origin in Sachen Bandbreitenbedarf und Traffic entlastet.

Der initiale Request auf http://www.foo.com wird damit aber nicht beschleunigt, die Latenz für den ersten Request bleibt hoch. Bei Webapplikationen mit wenig dynamischen Inhalten kann eine mögliche Lösung sein, dass der ganze Traffic durch das CDN geroutet wird. Dabei wird www.foo.com im DNS als CNAME Record auf foo.cdn.com eingetragen. Dadurch landen alle Requests auf dem CDN, und es können neben den statischen Assets auch ganze HTML-Seiten gecacht werden. In diesen Set-ups sollten sich Inhalte auf dem CDN gezielt invalidieren lassen, wenn sie veraltet sind – beispielsweise wenn die Redaktion eine neue Seite publiziert. Alle Anbieter haben für diesen Zweck ein API, das aus der Webapplikation angesprochen werden kann.

Webapplikationen mit hochdynamischen Inhalten sind heute eher die Regel, weshalb ganze HTML-Seiten selten gecacht werden können. Trotzdem kann es aus Sicht der Geschwindigkeit sinnvoll sein, den Traffic durch das CDN zu routen und zwar ohne Caching. Die CDN-Provider setzen dazu spezielle Techniken ein und behalten die TCP-Verbindungen von POP zu Origin permanent offen. Bei dynamischen Inhalten geht der Request dann zwar vom Browser via POP zur Origin, die mühsamen Roundtrips für das Öffnen der TCP-Verbindung entfallen aber.

Wenn man sich für den Einsatz eines CDN entschieden hat, muss ein Anbieter evaluiert werden. Dabei gibt es verschiedene Kriterien, die je nach Einsatzzweck unterschiedlich gewichtet werden müssen. Grundlegend ist sicherlich, dass der Anbieter eine gute geografische Abdeckung im Gebiet hat, wo der Großteil der Benutzer zu finden ist. Ist das nicht der Fall, kann das CDN seinen eigentlichen Nutzen nicht erfüllen. Ein weiterer Punkt bei der Evaluation ist die allgemeine Funktionalität: Wie genau kann man das Caching steuern? Wie können Inhalte invalidiert werden? Und nicht zuletzt: Gibt es Möglichkeiten für Reportings und sonstige Auswertungen? Zu guter Letzt muss eine Kosten-Nutzen-Rechnung gemacht werden. Viele CDN-Anbieter rechnen die Kosten nach Traffic-Volumen ab. Das bedeutet nichts anderes, als dass die Kosten für ein CDN linear mit der Nutzung steigen.

Latenz optimieren

Neben Optimierungen im Bereich Netzwerk und der physikalischen Distanz können wir weitere Faktoren beeinflussen. Wenn wir die Interaktion eines Benutzers mit unserem Web Service betrachten, dann können wir zwischen einer Pull- und Push-basierten Verbindung unterscheiden. Als gutes Beispiel dient hier der Live-Support-Chat. Ein solcher Chat sollte neue Nachrichten anzeigen, sobald diese abgesendet sind und nicht erst in einem bestimmten Poll Interval angefragt werden. Diese Echtzeitkommunikation kann z. B. mit einer WebSocket-Implementierung umgesetzt werden.

WebSocket öffnet eine TCP-Verbindung zwischen Server und Client und erlaubt beiden Parteien gleichzeitig den Datenaustausch. Die Verbindung bleibt offen, auch wenn keine Daten übermittelt werden und schafft damit eine neue Herausforderung für das Backend: keep-alive auf vielen parallelen Verbindungen ohne wirkliche Übertragung.

Das Pull-Verfahren kann ebenfalls Daten nahe der Echtzeit anzeigen, indem der Server in einem bestimmten Intervall angefragt wird, ob Änderungen zur Verfügung stehen. Im Vergleich zur Push-Variante erzeugen wir bei Pull viel mehr Anfragen und somit auch mehr Last auf dem Backend. Eine solche Lösung skaliert nur sehr schlecht mit einer zunehmenden Anzahl an Benutzern und niedrigem Pull Interval. Die Methode lässt sich beispielsweise mit einem Web Worker umsetzten. Der Web Worker ist ein Hintergrundprozess im Browser, unabhängig vom Page Rendering und dem JavaScript-Code und wird ausgeführt, solange sich der Benutzer auf der Seite befindet.

Terminologie

Die folgenden Begriffe werden bei der Diskussion von Performance genutzt. Die Literatur und der Sprachgebrauch sind nicht konsistent. Man sollte also bei Dokumentationen und Spezifikation die verwendeten Begriffe genau erläutern.

Eine Page View bezeichnet das Laden einer Seite mit allen benötigten Ressourcen. Ein Request ist der Abruf einer einzigen Ressource. Eine einzelne Page besteht immer aus einem einzigen HTTP-Request für das HTML sowie mehreren Requests für alle von der Seite für die Darstellung benötigten Inhalte (CSS Style Sheets, JavaScript-Dateien und Bilder). Eine Verfeinerung der Page View heißt „Above the fold“: Hier geht es darum, nur den Bereich zu messen, den ein Benutzer beim Laden der Seite tatsächlich sehen kann. Der Begriff kommt aus der Zeitungsbranche: Über dem Falz ist das, was die Leute morgens am Kiosk schon in der Zeitung sehen können, wenn sie noch im Regal liegt.

Die Performance ist die Zeit, die das System benötigt, um eine Transaktion auszuführen. Man muss für jedes System individuell definieren, was eine Transaktion ist. Man kann beispielsweise einen Request oder eine Page View als Transaktion definieren. Wichtig ist, dass es die Performance ist, die den Besucher interessiert. Ihm ist daran gelegen, dass seine Anfrage möglichst zügig beantwortet wird. Der Eindruck wird heute oft als Apdex angegeben. Die Latenz bezeichnet die Laufzeit von Traffic zwischen zwei Knoten. Da es schwierig sein kann, Latenz nur in eine Richtung zu messen, misst man meistens Roundtrip, also Hin- und Rückweg des Signals. Das gängigste Werkzeug zur Messung von Latenz ist Curl. Wenn man Curl aber auf das Websystem anlegt, spielt auch das Antwortverhalten dieses Systems eine Rolle. Oft wird die Latenz also der Performance gleichgesetzt.

Fazit

Die Latenz ist neben der Backend-Performance und der Kapazität ein wichtiger Faktor der User Experience unseres Websystems. Dabei ist vor allem darauf zu achten, dass die Maßnahmen zur Verbesserung auch den tatsächlichen Anforderungen entsprechen. Arbeitet ein System nur für den deutschen Markt, brauchen wir kein CDN in Asien. Die Anforderungen sind entsprechend genau zu erheben. Die lokale Performance ist außerdem kein Selbstzweck. Poweruser können mit mehreren Sekunden Latenz leben, wenn sie nicht den ganzen Tag mit der fraglichen Funktion arbeiten müssen. Unterm Strich sollte man aber Antwortzeiten von mehr als einer Sekunde vermeiden. Manchmal braucht es hierfür nur ein wenig strukturierte Analyse und selbstverständlich eine genaue Einstellung der HTTP-Cache-Header.

Verwandte Themen:

Geschrieben von
Daniel Takai
Daniel Takai
Daniel Takai ist Enterprise-Architekt, arbeitet in der Schweiz und ist auf die digitale Transformation spezialisiert. Er berichtet regelmäßig im Java Magazin über seine Erfahrungen und Erkenntnisse. Im Frühling 2016 erscheint sein Buch über Methoden der Entwicklung von Cloud-basierten und serviceorientierten Websystemen.
Nicolas Bär
Nicolas Bär
Nicolas Bär arbeitet als IT-Architekt bei der Unic AG in Zürich. Als Performanceenthusiast optimiert er Webapplikationen und engagiert sich in seiner Freizeit im Open-Source-Umfeld.
Christian Wittwer
Christian Wittwer
Christian Wittwer ist IT-Architekt bei der Unic AG in Bern. Er kümmert sich mit seinem Team um die Performance von Webapplikationen, von der Entwicklung bis in den Betrieb.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.