In diesem Beitrag geht es kurz und übersichtlich um den Ablauf beim Abrufen dynamischer Webseiten. Es werden auch einige Aspekte zur Optimierung betrachtet. Es ist hilfreich, bei der Erstellung von Webseiten dies nie aus den Augen zu verlieren.

Wie dynamische Webseiten entstehen

Unter dynamischen Webseiten werden Seiten verstanden, deren endgültige, an den Server gesendete Form erst im Augenblick des Abrufes entsteht. So können Daten interaktiv in die Seiten eingebaut werden. Der Vorteil besteht vor allem in der Möglichkeit, auf Nutzereingaben reagieren zu können. Formulare lassen sich sofort auswerten und schon die nächste Seite kann den Inhalt wiedergeben oder Reaktionen darauf zeigen. Die Anwendungsmöglichkeiten sind fast unbegrenzt. Ob und in welchem Umfang außerdem Datenbanken zum Einsatz kommen, hängt von der Zielstellung ab. Dynamische Webseiten an sich benötigen keine Datenbank. Sie sollten sich vor allem als Anfänger nicht dem Zwang unterziehen, gleich jedes Problem mit der Hilfe einer Datenbank zu lösen, auch wenn Profis dies bevorzugen werden. Im Buch werden viele Beispiele gezeigt, die mit einfachsten Mitteln beeindruckende Effekte erzielen — ganz ohne Datenbank. Die Entstehung einer dynamischen Website wird in der Abbildung unten erläutert. Dieser Ablauf sollte unbedingt verstanden werden, denn alle anderen, teilweise komplexeren Vorgänge in der Programmierung bauen darauf auf.

Wenn der Benutzer eine Adresse im Browser eintippt, läuft ein recht komplexer Vorgang ab:

  1. Der Browser sucht einen Nameserver, um die IP-Adresse zum URL zu ermitteln
  2. Der Nameserver konsultiert gegebenenfalls weitere Server, um die IP-Adresse zu beschaffen
  3. Der Browser erhält eine IP-Adresse des Servers. Wenn das Protokoll HTTP verwendet wird, ist damit auch die Portadresse festgelegt (Port 80). IP-Adresse und Port bilden eine sogenannte Socket.
  4. Der Browser hat eine IP-Adresse vom Provider erhalten und einen Port für die Verbindung gebildet. Damit steht auch ein Socket zur Verfügung. Zwischen beiden Endpunkten kann nun IP-Verkehr stattfinden.
  5. Der Browser sendet über diese Verbindung die Anforderung der Seite. Die erfolgt mit dem Protokoll HTTP, die entsprechende Methode lautet GET, der Vorgang wird „Request“ oder „Anforderung“ genannt.
  6. Der Server empfängt die Anforderung und sucht die Datei. Wird sie gefunden, liefert er sie aus. Dieser Vorgang wird „Response“ genannt, in diesem Text auch „Antwort“. Wird die Datei nicht gefunden, erzeugt der Server einen Fehler. Für nicht vorhandene Dateien definiert HTTP die Fehlernummer 404.
  7. Der Browser empfängt Daten oder eine Fehlermeldung und zeigt diese an.

Zuerst fordert also der Nutzer mit seinem Browser ein Programm an. Der gesamte Vorgang ist letztlich clientgesteuert. Auf dem Webserver wird Anfrage angenommen und HTML-Code oder auch statischer Inhalt erzeugt. Die fertige Seite wird dem Webserver zurückgegeben, der sie dann an den Browser sendet. Damit ist der Vorgang beendet. Beide Seiten „vergessen“ alles, was beim Ablauf verwendet wurde. Mit der Anforderung des nächsten Objekts wird der gesamte Ablauf wiederholt. Die Vorgänge der Namensauflösung und Adressbeschaffung laufen völlig transparent ab und sind bei der Programmierung kaum zu berücksichtigen. Der eigentliche Zusammenbau der Seiten ist der interessante Teil. Dies passiert in der Darstellung der Schrittfolge im Schritt 6. Diesen Punkt gilt es also genauer zu untersuchen.

Für alle Probleme liefern diverse Programmierumgebungen interessante und hilfreiche Lösungen. Die Programmierung ist deshalb vergleichsweise einfach. Das ändert aber nichts am Prinzip oder der zugrunde liegenden Technik. Ohne das Ping-Pong-Spiel zwischen Browser und Webserver funktioniert nichts. Manchmal ist es nicht immer sichtbar, dass dieser Prozess tatsächlich abläuft, aber er wird dennoch ausnahmslos ausgeführt. Klar sollte sein, dass in der Webseite, wenn sie an den Browser gesendet wurde, nur noch HTML-Code steht.

Optimierung

Bei der Optimierung gibt es viele Aspekte zu beachten:

  • Entwicklungsumgebung: Ohne die richtigen Werkzeuge wird das nichts!
  • Server-seitige Optimierung: Requests, Bandbreite, Payload
  • Client-seitige Optimierung: DOM, JavaScript

Werkzeuge

Verstehen Sie, wie das Web funktioniert. Dazu gehört neben den bereits behandelten Protokollen ein grundlegendes Verständnis für Performance, Bandbreite, Anzahl der HTTP-Requests, das Verhalten von HTML und JavaScript im Browser und schlussendlich das Rendering (Darstellung) und CSS-Effekte. Diverse Werkzeuge helfen dabei, die Vorgänge sichtbar zu machen und dies hilft erheblich dem Verständnis.

Folgende Werkzeuge sollten Sie haben:

  • Fiddler:
    • Von Telerik, für Windows (http://www.telerik.com/fiddler)
    • Fiddler on Mono (http://fiddler.wikidot.com/mono) für Linux
  • F12-Tools in IE, Chrome, Firefox, Edge usw. Damit haben Sie:
    • Profiler
    • Netzwerkanalyse
    • CSS-Debugger
    • HTML-Baum
    • JavaScript Debugger
  • Vieles auch online verfügbar, beispielsweise
Ablauf der Kommunikation auf einem Zeitstrahl
Ablauf der Kommunikation auf einem Zeitstrahl

Fiddler ist ein Web-Debugger und Protokoll-Proxy. Damit können Sie den Verkehr zwischen Client und Server auf der Client-Maschine sehen und auswerten. In der Ansicht für den zeitlichen Ablauf ist zu erkennen, dass die Anfragen nur teilweise parallel ablaufen. Einige Abfragen werden erst dann ausgelöst, wenn der Browser mit der Verarbeitung der Seite begonnen hat.

xxx
Ablauf der Kommunikation in Fiddler

Diese Effekte können beim Aufbau der Seite berücksichtigt werden. Je weniger Abhängigkeiten bestehe, je besser wird die zur Verfügung stehende Bandbreite ausgenutzt.

Serverseitige Optimierung

Bei der serverseitigen Optimierung geht es um folgende Themen:

  • Pipeline Optimierung
  • Prozesskonfiguration
  • CDN
  • Bundling
  • Sprites

Pipeline Optimierung

Im Kern geht es hier um das Entladen (Deaktivieren) von unnötigen Modulen. Webserver kommen mit einer ganzen Reihe von Modulen, die alle möglichen Aufgaben erfüllen. Das beginnt mit der Authentifizierung und endet mit einfacher Protokollierung. Es liegt in der Natur der Module, dass diese jede Anfrage behandeln. Geringe Verzögerungen wirken sich dann drastisch aus.

Für ASP.NET und die IIS sieht dies beispielsweise folgendermaßen aus. Zuerst die standardmäßig aktiven Module:

Module im IIS -- nicht optimiert
Module im IIS — nicht optimiert

Möglicherweise wird aber nur ein Teil davon wirklich benötigt:

Module im IIS -- optimiert
Module im IIS — optimiert

Wenn Sie mit Apache auf Linux arbeiten, nutzen Sie das Kommando a2dismod:

$ sudo a2dismod autoindex

Typische Module, die nicht immer benötigt werden, sind folgende:

  • PHP
  • SSL
  • Rewrite
  • Perl
  • Python
  • Rack/Ruby/Passenger

Prozesskonfiguration

Das Ziel hier ist die optimale Nutzung von Ressourcen. Dazu passen Sie die Prozesskonfiguration an die konkreten Hardwarebedingungen an. Für einen Windows-Server mit IIS umfasst die folgende Schritte:

  • maxWorkerThreads
    20 pro Kern (4 Kerne == 80 Threads). Höher ist möglich, wenn wenige CPU-intensive Arbeit (z.B. viel Async, Service Calls), dann sind Werte bis 100 möglich.
  • maxIOThreads
    20 pro Kern. Für Dateioperationen, Datenbankzugriff, Web Service Calls, interne Requests etc. Bei schneller Hardware, 10 Gbit Netz, SSD etc, sind Werte bis 100 möglich.
  • minWorkerThreads, minIOThreads
    Standard 1. Steuert Beginn der Request-Warteschlange, mindestens der Wert muss frei sein, damit NICHT gequeued wird.
  • memoryLimit
    Anteil (%) vom Systemspeicher, den der Worker-Prozess belegen darf. Wenn die Anwendung alleine ist, darf der Wert hoch sein, z.B. 80. Bei vielen Lecks oder anderen Anwendungen, vor allem COM, Wert verringern.

Die Einstellungen der Prozesskonfiguration erfolgt in der machine.config.

Einstellungen der Prozesskonfiguration
Einstellungen der Prozesskonfiguration

Im Apache stellen Sie vergleichbare Wert wie folgt ein:


    ServerLimit          40
    StartServers          2
    MaxClients          1000
    MinSpareThreads      25
    MaxSpareThreads      75 
    ThreadsPerChild      25
    MaxRequestsPerChild   0

CDN (Content Delivery Network)

Ein Content Delivery Network (manchmal Content Distribution Network genannt), ist ein Netz regional verteilter und über das Internet verbundener Server, mit dem Inhalte — insbesondere große Ressourcen wie Skripte, Bilder oder Videos — ausgeliefert werden. Das Ziel eines CDN sind schnellere Antworten auf Requests und weniger Latenz. Für allgemeine Dateien, wie jQuery, Knockout etc. bietet sich Microsoft, Google etc. an. Für eigene Ressourcen gibt es kostenpflichtige Dienste wie Cachefly (einfach, Upload/Distribute) oder EdgeCast (komplex, DNS Caching).

Prinzip eines Content Delivery Network
Prinzip eines Content Delivery Network

Minifizierung und Bundling

Bundling und Minifizierung sind zwei Techniken, mit denen die Ladezeit verbessert werden kann. Dies erfolgt im Wesentlichen über das Zusammenfassen von Ressourcen (Bundling) und damit die Vermeidung von Anfragen. Hintergrund ist, dass aufgrund des Overheads des Protokolls HTTP die Auslieferung vieler kleiner Dateien sehr viel mehr Bandbreite erfordert, als die Auslieferung einer großen Datei.

Das Erstellen von Sprites ist manuell kaum beherrschbar. Es gibt deshalb eine Vielzahl von Werkzeugen für alle Plattformen und Betriebssysteme. Idealerweise richten Sie diese Werkzeuge als Teil des Erstellungsvorgangs ein. Wie das aussieht, hängt vom benutzten Entwicklungssystem ab.

Sprites

Als Sprite oder CSS-Sprite bezeichnet man eine einzelne Grafikdatei, die mehrere Symbole und Bildbausteine enthält. Diese zusammengefassten Grafiken fungieren als Bildlieferant und dienen dazu, die Ladezeit von Webseiten zu minimieren. Die einzelnen Elemente dieser Gesamtgrafik werden mit den CSS-Eigenschaften background-image und background-position ein- beziehungsweise ausgeblendet und platziert.

Hintergrund ist, dass aufgrund des Overheads des Protokolls HTTP die Auslieferung vieler kleiner Bilder viel mehr Bandbreite erfordert, als die Auslieferung eines großen Bildes.

Beispiel für CSS, welches mit Sprites arbeitet:

.flags-canada, .flags-mexico, .flags-usa {
  background-image: url('../images/flags.png');
  background-repeat: no-repeat;
}

.flags-canada {
  height: 128px;
  background-position: -5px -5px;
}

.flags-usa {
  height: 135px;
  background-position: -5px -143px;
}

.flags-mexico {
  height: 147px;
  background-position: -5px -288px;
}

Das Erstellen von Sprites ist manuell kaum beherrschbar. Es gibt deshalb eine Vielzahl von Werkzeugen für alle Plattformen und Betriebssysteme. Unter Node oder auf einem System, das Node installiert hat, nutzen Sie beispielsweise Sprity. Dies wird zuerst installiert:

$ npm install sprity -g

Dann fassen Sie alle Bilder eines Ordners zu einem Sprite zusammen:

$ sprity ./outputfolder/ ./inputfolder/*.png

T> Mehr finden Sie beispielsweise unter https://css-tricks.com/css-sprites.

Allgemeines und Banales

Generell sollten Sie darauf achten, bestimmte Techniken nicht zu benutzen:

  • Vermeiden Sie Redirects
  • Vermeiden Sie Frames/IFrames
  • Vermeiden Sie DNS-Lookups (absolute Pfade)

Verteilen Sie Assets auf verschiedene Hosts (sogenanntes Off Loading):

  • www.greatsite.de
  • images.greatsite.de
  • scripts.greatsite.de

Der Hintergrund ist, dass Browser pro Host nur eine bestimmte Anzahl Anfragen parallel aussenden (zwischen 6 und 13). Haben Sie drei Hosts, verdreifachen Sie die verfügbare Bandbreite.

Bei statischen Assets sollten Sie Cookies und Header vermeiden, wenn es geht. Nutzen Sie unbedingt Gzip/Deflate zur Komprimierung. Das ist standardmäßig bei praktisch allen System aktiviert. Konfigurieren Sie den Server dann aber so, das besser Gzip benutzt wird. Das ETag (Entitätsmarke) wird evtl. nicht benötigt — dieses Kopffeld kann man dann entfernen.

Clientseitige Optimierung

An dieser Stelle soll nur eine kurze Übersicht der Möglichkeiten als Anregung erfolgen. Sie finden zu allen Maßnahmen reichlich Beispiele im Internet.

Umgang mit Bildern

Wenn moderne Browser möglich sind, benutzen Sie Inline-Bilder. Vor allem für dynamische Bilder, seltene, oder sich häufig Ändernde ist dies vorteilhaft.

<img src="data:image/gif;base64, R0lGODlh...
    ....    und so weiter  .... "> 

Für die Kodierung nach Base64 eignet sich unter anderem:

  • http://webcodertools.com/imagetobase64converter

Sie sollten niemals Bilder skalieren, sondern immer vorher berechnen (auf dem Server) und komprimiert ausliefern.

Nutzen Sie Font-Bibliotheken, sogenannte Symbolfonts, wenn möglich. Fontbasierte Symbole vermeiden das Problem, das einzelne Symbolbildchen zu einer Flut weiterer Anforderungen auf dem Server führen. Stattdessen werden alle Symbole als ein Font geladen — also in einer Datei. Allerdings ist ein Symbol dann wie ein Buchstabe. Er ist in der Größe und Ausdehnung veränderbar, kann aber nur eine Farbe annehmen. Fonts sind zudem meist flach, 3D-Effekte scheiden hier aus. Für schnelle, moderne Webseiten haben sich Glyphs jedoch weit etabliert.

Einige Beispiele:

Dies ist freilich nur eine kleine Auswahl und soll dazu anregen, vor den ersten Designversuchen die passende Unterstützung zu suchen.

Der freie Symbolfont Octicons
Der freie Symbolfont Octicons

Wenn Sie JPG-Dateien nutzen entfernen Sie Junk-Daten. Das sind Meta-Informationen, die Kameras hinzufügen, teilweise aber auch Programme wie Photoshop. In den Bildern stecken dann kilobyteweise Informationen zum Aufnahmort, Datum, Kameradaten usw.

Achten Sie generell auf das passende Bildformat.

Bildgröße bei Bildern mit Farbverläufen
Bildgröße bei Bildern mit Farbverläufen

Umgang mit dem DOM

JavaScript als Sprache ist extrem schnell. Was problematisch ist, ist der Zugriff auf Elemente im Objektbaum der Seite (document object model, DOM). Ein Beispiel aus jQuery soll dies illustrieren:

$('#dialog-window')
  .width(600)
  .height(400)
  .css('position': 'absolute')
  .css('top', '200px')
  .css('left', '200px'); 

Hier wird ein Element #dialog-window adressiert und dann wird sechsmal auf dieses Element zugegriffen. Dies ist sehr unglücklich. Besser ist folgender Zugriff, der alle Werte in einem Zugriff ändert:

$('#dialog-window').css({ 
   width: '600px', 
   height: '400px', 
   position: 'absolute', 
   top: '200px', 
   left: '200px' }); 

Erstellt wird hier ein Batch beim Rendern. Erstellen Sie dynamische DOM-Blöcke separat und fügen Sie dann den gesamten Baum in einem Schritt ein. Einzeln führt dies zu vielen Zeichenvorgängen, zusammengefasst wird nur ein Auffrischen des Bildschirms ausgelöst.

Hintergrund ist, dass der Browser bei jeder Änderung am DOM die Oberfläche neu zeichnet. Sequenzen mit viele kleinen Änderungen erfordern eine erhebliche Rechenleistung. Auch wenn diese vermeintlich zur Verfügung steht, heißt es dennoch, dass bei mobilen Geräten viel Strom und damit Akkulaufzeit verbraucht wird.

Dynamische Webseiten