Serie High Performance Websites – Teil 1: Vorarbeiten

This entry is part 1 of 6 in the series High Performance Websites

Nachdem die MySQL Performance Serie ein großer Erfolg war, haben wir uns dazu entschlossen eine weitere Reihe mit dem Titel “High Performance Website” aufzulegen.

In dieser Blogserie versuchen wir Anregungen zu liefern auf welche Einstellungen zurückgegriffen werden kann um lahmenden Webseiten Beine zu machen oder zu erwartende Lastspitzen besser abzuarbeiten. Ziel ist es dem Anwender in einem möglichst kleinem Zeitfenster die gewünschte Website auszuliefern. Hierbei ist wichtig dem Gast möglichst schnell eine Darstellung im Browser zur Verfügung zu stellen. Ein Ladebalken in der Statusleiste des Browsers ist hierbei die schlechteste der vorhandenen Möglichkeiten.

Um mögliche Veränderungen wahrnehmen zu können ist wichtig diese auch zu protokollieren. Für die technischen Messwerte wie Ladezeit oder HTTP-Header können Tools wie Firebug oder YSlow unter Firefox sowie die integrierten Developmenttools von Apple`s Safari unterstützen. Zum anderen sollten zusätzlich zu den gemessenen Werten auch Konfigurationsänderungen und subjektive Wahrnehmungen beim Aufruf der Seite wie z.B. verlangsamt ladende Bilder oder ein verzögerter Aufbau des Seiteninhaltes dokumentiert werden.

In den nächsten Artikeln geben wir einen Einblick in diverse Möglichkeiten um den Aufruf zu beschleunigen. Hier werden sowohl Server- als auch Codeseitige Verbesserungen dargestellt und beschrieben, im Detail werden folgende Themen behandelt:

  • Teil 1: Vorarbeiten und Grundlagen
  • Teil 2: Minimieren von HTTP-Requests
  • Teil 3: Einbinden von Expires-Header
  • Teil 4: Komprimierung der Seiten
  • Teil 5: DNS Lookups und Redirects
Wir hoffen die aufgezeigten Lösungen helfen Ihnen weiter. Für Rückfragen zu den genannten Themen stehen wir selbstverständlich gerne zur Verfügung.

Serie High Performance Websites – Teil 2: Minimieren von HTTP-Requests

This entry is part 2 of 6 in the series High Performance Websites

Im ersten technischen Teil der High Performance Websites Serie dreht sich alles um die Reduzierung von HTTP-Requests.

Interessant zu wissen ist, dass bei den meisten Webseiten (bei leerem Browser Cache) weniger als 10-20% der Antwortzeit für den eigentlichen Abruf der HTML Dokumente verwendet wird. Im Umkehrschluss werden dann 80-90% der Ladezeit durch HTTP-Requests verbraucht. Durch dieses Verhältnis wird deutlich das die Anzahl der HTTP-Anfragen zur Ladezeit der gesamten Webseite und damit auch zur Gesamtperformance bedeutend beiträgt.

Um unnötige Requests zu vermeiden gibt es mehrere Möglichkeiten, eine davon ist mehrere vereinzelte Bilder zu einem Ganzen zusammenzufassen und mittels Imagemap oder CSS-Sprite in einzelne Links zu unterteilen.

Genauer beleuchtet kann z.B. die Navigationsleiste einer Webseite aus vielen einzelnen Bildern bestehen wodurch für jeden Abruf eines Bildes ein eingener HTTP-Request erzeugt wird. Werden nun diese einzelnen Bilder zusammengefasst entsteht nur noch eine einzige HTTP-Anfrage.

Eine weitere Möglichkeit ist die direkte Einbettung von Bildern in eine HTML oder PHP Seite mittels sog. Inline-Images. Dabei wird direkt im Sourcecode der Seite das “data:” URI Schema wie folgt verwendet:

HTML URI Schema:

<img src="data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGP
C/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IA
AAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1J
REFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jq
ch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0
vr4MkhoXe0rZigAAAABJRU5ErkJggg==" alt="Red dot" />

Siehe auch: http://en.wikipedia.org/wiki/Data_URI_scheme

Bei der Verwendung von Inline-Images ist hervorzuheben das diese nicht Seitenübergreifend gecached werden was bedeutet das jede Seite die das Bild enthält auch um dessen Größe im Cache anwächst.

Leider werden Inline-Images nicht von allen Browsern verarbeitet, dazu gehört unter anderem der Internet Explorer, daher wird diese Variante eher selten benutzt. Imagemaps bzw. CSS-Sprites sind also den Inline-Images vorzuziehen.

Abschließend soll auch noch auf die Kombination von einzelnen JavaScript und Stylesheets hingewiesen werden. Hier ist es empfehlenswert die jeweils voneinander getrennten einzelnen JavaScript und Stylesheet includes zu vereinheitlichen sodass noch jeweils ein gesamtes Script geladen werden muss. Auch diese Maßnahme verringert HTTP-Anfragen.

Serie High Performance Websites – Teil 3: Cachen erlaubt !

This entry is part 3 of 6 in the series High Performance Websites

Nachdem im vorherigen Teil die HTTP-Requests auf ein minimum reduziert wurden ist es nun an der Zeit den Client das zwischenspeichern von Inhalten auf eine besitmmte Zeit hin zu erlauben.

Hierfür werden sog. Expires-Header serverseitig konfiguriert. Die dadurch in den HTTP-Response gesetzten  “Expires” und “Cache-Control” Header werden bei der Auslieferung jedes Contentelements übertragen. Definiert wird der Expires-Header auf Basis von Dateinamen oder Pfaden in der abgerufenen URL. Um die Expires in Apache nutzen zu können muss mod_expires aktiviert sein.

Für Lighttpd ist eine passende Anleitung unter http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModExpire zu finden.

Zusätzlich ist es notwendig in der Konfiguration des entsprechenden VirtualHost’s folgende definitionen vorzunehmen:

<IfModule mod_expires.c>
ExpiresActive on
ExpiresByType image/gif "access plus 10 years"
ExpiresByType image/jpeg "access plus 10 years"
ExpiresByType image/png "access plus 10 years"
ExpiresByType text/css "access plus 2 years"
ExpiresByType text/js "access plus 2 years"
ExpiresByType text/javascript "access plus 2 years"
ExpiresByType application/javascript "access plus 2 years"
ExpiresByType application/x-javascript "access plus 2 years"
</IfModule>

Hier wird für die genannten Elemente jeweils eine Ablaufzeit von 10 bzw. 2 Jahren definiert, was den anfragenden Browser dazu bringt das geladene Element für 10 bzw. 2 Jahre im Browsercache zu halten und nicht erneut abzufragen.

Zusätzlich kann noch ein genereller Expire mittels

ExpiresDefault ”access plus 1 days”

gesetzt werden.

Durch dieses Verhalten ergibt sich allerdings ein neues Problem, beispielsweise kann es sein das sich innerhalb dieser 2 Jahre das Stylesheet verändert. In diesem Fall würde der Browser das veränderte CSS durch die ihm Bekannte “Cache-Halbwertszeit” von 2 Jahren nicht neu laden und die Änderung würde nicht angezeigt. Um dieses Problem zu verhindert sollte eine Versionierung der “halbstatischen” Elemente durchgeführt werden, aus “style.css” wird so “style-20090101-001.css”.

Ändert sich nun das Stylesheet auf “style-20090101-002.css” wird der Sourcecode der HTML Seite um den Namen des CSS angepasst und der Browser läd automatisiert die neue Version da ihm das “neue” Inhaltselement nicht im Cache bekannt ist.

Serie High Performance Websites – Teil 4: Weniger ist Mehr !

This entry is part 4 of 6 in the series High Performance Websites

Ein weiterer wichtiger Punkt bei der Optimierung ist das Thema Kompression, hierbei werden sämtliche noch komprimierbaren Inhalte (HTML, CSS, JavaScript, …) geschrumpft und so in einer verkleinerten Version ausgeliefert.

Die durchschnittliche Einsparung durch Komprimierung beträgt bis zu 70% was bedeutet das nur 30% des ursprünglichen Dateiinhaltes übertragen werden müssen. In den meisten Setups wird “gzip” für die Verkleinerung der ܜbertragungmenge verwendet, gzip bietet hier eine bessere Komprimierungsrate als “deflate”.

Um die Komprimierung in Apache zu aktivieren muss mod_deflate geladen werden, zusätzlich zum laden des Moduls muss in der VirtualHost (oder globalen) Konfiguration die Kompression aktiviert werden.

AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/x-javascript

Zusätzlich lässt sich mit dem Parameter DeflateCompressionLevel der Komprimierungsgrad festlegen, die Grundeinstellung ist hier meist ausreichend.

Einige ältere Browserversionen unterstützen leider keine Kompression, deswegen sollten zusätzlich folgende Einstellungen hinterlegt werden um auch diesen “Exoten” Zugang zur Webpräsenz zu ermöglichen.

BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

Durch die nun gesetzten Einstellungen werden sämtliche “textuellen” Inhalten wie HTML/CSS oder JS ab jetzt komprimiert übertragen. Im nächsten Teil wird nun noch angeführt wie mittels DNS, Keepalive und der vermeidung von Redirects noch zusätzlich Ladezeit verkürzt werden kann.

Analog hierzu ist die Konfiguration unter Lighttpd, das “deflate” Modul ist vollständig unter http://redmine.lighttpd.net/projects/lighttpd/wiki/Mod_Deflate dokumentiert. Die Einrichtung ist hier denkbar einfach sofern Lighttpd mit Kompressionsunterstützung übersetzt wurde (sollte in allen Distributionspaketen Standard sein).

Serie High Performance Websites – Teil 5: letztes Feintuning

This entry is part 5 of 6 in the series High Performance Websites

Dieser vorerst finale Teil befasst sich mit den restlichen Serverseitigen Themen Redirects und Keep-Alive. Zusätzlich wird auf DNS und das DNS Cacheverhalten des Browsers eingegangen.

Für die Performance von Webseiten hat DNS ausschließlich beim erstmaligen Aufruf Einfluss. Hier ist zu beachten das der DNS Server ihres ISP typischerweise 10-200 Millisekunden braucht um eine Namensauflösung durchzuführen. Nach der Erstanfrage wird der DNS Eintrag sowohl im Betriebssystem als auch im Browser gecached um erneute Anfragen zu vermeiden. Wie lange der bereits erfragte Eintrag zwischengespeichert wird gibt in erster Linie die TTL (Time-To-Live) in der DNS Zone an, desweiteren ist in den Browsern hinterlegt wie lange der DNS Cache zusätzlich zur TTL Einträge speichern soll. Dies kann bedeuten das trotz bereits abgelaufener TTL ein DNS Eintrag im Browser noch durch den Cache vorgehalten wird.

In Summe kann festgehalten werden das es sinnvoll ist die Verwendung unterschiedlicher Hostnamen auf ein minimum zu reduzieren um die durch DNS Anfragen entstehende Latenz beim erstmaligen Laden der Seite zu reduzieren.

In den vorherigen Kapiteln wurde vieles unternommen um die Ladezeit für den Benutzer so gering wie möglich zu halten. Genau das Gegenteil verursachen leider verwendete Redirects, wird bei der ersten HTTP Anfrage ein Redirect vom Server erwidert folgt daraus ein erneuter (also zusätzlicher) HTTP Request. Dieses Verhalten verzögert das Laden der eigentlichen Seite und sollte vermieden werden.

Ziel ist also Redirects wenn möglich zu verhindern oder sehr eingeschränkt zu verwenden.

Zur weiteren Optimierung sollte Keep-Alive serverseitig aktiviert werden, hier werden vom Browser bereits offene TCP Verbindungen für einen definierten Zeitraum wiederverwendet. Dieser Mechanismus spart zusätzlich Zeit für den eigentlichen TCP Verbindungsaufbau. Der Server und Browser signalisieren beim initiieren der Verbindung im “Connection” Header ob Keep-Alive Verbindungen erlaubt sind. Zusätzlich zu Keep-Alive wird unter HTTP/1.1 Pipelining zur Verfügung gestellt um die Verbindungsgeschwindigkeit zu erhöhen. Hier werden mehrere Anfragen über einen Socket übermittelt ohne vorher auf eine Antwort des Servers warten zu müssen.

In Apache wird Keep-Alive durch die folgenden Direktiven aktiviert.

Keep-Alive on