Das hatten wir doch schon?

Website-Optimierung per Caching
Kommentare

Jeder Webentwickler, der ein Projekt in das Internet stellt, wünscht sich, dass sein Projekt einmal so erfolgreich ist, dass der Server, auf dem es läuft, nicht mehr ausreicht, all die Besucher zu versorgen, die das Angebot nutzen wollen. Da es aber langweilig wäre, diese Grenze schon mit ein paar Usern am Tag zu überschreiten, versucht man, über Caches in der Anwendung, sie ein wenig leistungsstärker zu machen.

Caching ist eine der Königsdisziplinen, wenn es um das Optimieren einer Webseite geht, denn wenn es richtig gemacht ist, kann man mit sehr wenig Aufwand sehr viel erreichen. Umgekehrt kann man aber auch mit viel Aufwand die Applikation unwartbar bekommen. Es gilt also, den richtigen Cache an der richtigen Stelle einzusetzen, um somit höchste Effizienz zu erreichen. Ideen, wo und wann man handeln sollte, soll diese Einführung in das Thema Caching bringen. Prinzipiell geht es beim Caching darum, dass man häufig verwendete oder relativ teuer zu berechnende Daten im System vorhält, damit sie auf Wunsch abrufbar sind. Der Einsatz von solchen Zwischenspeichern sollte aber gut überlegt werden. Nicht alles, was zu cachen ist, sollte auch seinen Weg in den Speicher finden, denn oft verliert man durch komplexere Strukturen das Stück Wartbarkeit, das genauso über den Erfolg eines Projekts entscheiden kann. Stolpersteine, die einem beim Caching hier gerne in den Weg gelegt werden, haben meistens ihren Ursprung in der Invalidierung, also dem Anstoßen der Neuberechnung, oder dem Aufwärmen des Zwischenspeichers. Wie reagiert mein System, wenn es das erste Mal neu gestartet wurde und somit keine Caches gefüllt sind? Beide Punkte sollte man unbedingt im Hinterkopf behalten, wenn man sich an die Optimierung einer Anwendung macht.

Drei Wege zum Glück

Es gibt viele Strategien, wie man Websysteme performant hält – wir werden drei Ansätze vorstellen. Ziel dabei ist es, einen Überblick über die Möglichkeiten zu geben und Interesse zu wecken. Auch wenn wir PHP-Entwickler von Herzen sind, sind all die vorgestellten Methoden auch für andere Sprachen, die im Web Anwendung finden, gültig. Wie bereits erwähnt, ist es eine der großen Herausforderung beim Cachen, die Stelle zu finden, an der eine Vorberechnung am effektivsten wirken kann, wobei wir Effektivität im Sinne der Entlastung der Infrastruktur verstehen. Die Last von den Servern zu nehmen und die Auslieferung der Daten zu beschleunigen, ist hier maßgeblich. Durch den Aufbau des Internets bietet sich an, an diversen Schichten eine Caching-Strategie zu wählen. Die Pyramidenform wurde gewählt, da sie am einfachsten den Wirkungsgrad des Caches zeigt. Je früher meine Vorberechnungen greifen, desto höher ist die Entlastung des Systems. Denn im besten Fall hilft einem der Client bereits Daten vorzuhalten, man denke nur an das Vorhalten von CSS- oder JavaScript-Dateien. Diesen Cache bezeichnen wir im Webumfeld als Browser-Cache. Bei nichtpersonalisierten Inhalten hilft uns eine weitere Schicht, die Effizienz zu erhöhen, indem sie stabilen Inhalt vorhält und ihn ohne Berechnungen ausliefern kann. Diese Strategie hört auf den Namen Webcache. Erst nachdem diese zwei Schichten erfolglos durchquert wurden, schlagen die Anfragen bei der Applikation ein. Hier werden punktuell Berechnungen zwischengespeichert und der Anwendung zur Verfügung gestellt. Applikation-Cache heißt hier das Zauberwort. Browser-Cache und ein einfacher Webcache können fast unabhängig von der Anwendung eingesetzt werden, was den großen Vorteil mit sich bringt, dass man in den Produktivcode nicht eingreifen muss und somit eine nachträgliche Verwendung in den meisten Fällen problemlos geschehen kann.

Caching im Client (Browser-Cache)

Der Freund jeder Applikation ist das Caching im Client. Hier wird die erste und effektivste Caching-Stufe eingebaut. Für den Einsatz des Browsers als Cache für eine Applikation muss der Entwickler nur einigen wenigen Regeln folgen und im Vorfeld bestimmen, welche Teile der Applikation wann und wie lange im Browser gecacht werden sollen. Das Caching-Verhalten eines Browsers wird über das Setzen von HTTP-Headern gesteuert.Dabei gibt es grundsätzlich zwei Methoden. Die erste arbeitet mit If-Modified-Since oder entity tag (ETag). Dem Browser wird über den Last-Modified-Header beim Aufruf einer Webseite mitgeteilt, wann sie das letzte Mal aktualisiert wurde. Er sendet dann bei jeder nachfolgenden Anfrage nach genau diesem Inhalt den Header: If-Modified-Since mit dem Änderungsdatum des Inhalts zur Überprüfung an den Server, der das gesendete Datum mit dem aktuellen Änderungsdatum vergleicht. Anschließend wird entweder die aktuelle Version des Inhalts gesendet oder ein „günstiger“ 304 (Not-Modified) Statuscode. Das Vergleichen über ein ETag funktioniert ähnlich. Der Webserver erstellt einen Hash-Wert aus den Dateiinformationen eines Inhalts und sendet ihn an den Browser. Dieser Wert wird dann wieder bei Folgeanfragen an den Server gesendet, der nach einer Überprüfung die gleichen HTTP-Statuscodes ausliefert wie bei der If-Modified-Since-Methode – Header: 200 oder 304. Per Standardeinstellung des Apache-Webservers wird der Hash-Wert aus I-Node-Nummer (Inode), Datum und Uhrzeit der letzen Änderung (Mtime) und der Größe der Datei in Bytes (Size) erstellt. Ein Problem entsteht bei der Verwendung von ETag, falls die Serverinfrastruktur aus mehr als einem Server besteht, da ein Hash-Wert aus den beschriebenen drei Parametern, InodeMtime und Size, erstellt auf mehreren Servern, unterschiedlich sein kann und damit das Caching ausgehebelt wird. Man könnte den Hash-Wert nur aus der Größe berechnen, allerdings läuft man somit Gefahr, bei Änderungen, die die Größe nicht verändern, den Browser-Cache nicht zu invalidieren. Die Lösung dieses Problems wird in einem späteren Abschnitt über die Verwendung von Expire vorgestellt. Das größte Problem ist jedoch, dass immer ein HTTP-Request gesendet wird und der Server Berechnungen vornehmen muss. Das wollen und können wir mit der zweiten Methode verhindern: Die Inhalte werden zuerst in statische und dynamische Inhalte aufgeteilt. Als statische Inhalte gelten zum Beispiel JS-, CSS-, und Bild-Dateien. Es bietet sich eine saubere Trennung der Applikation von ihren statischen Inhalten an. Wenn die Applikation von der Domain www.example.com ausgeliefert wird, können alle statischen Inhalte zum Beispiel von stat.example.com ausgeliefert werden. All diese statischen Inhalte sollten ein Jahr (maximale, im RFC definierte Zeit) lang gecacht werden. Die Frage, die jetzt geklärt werden muss, ist: Wie kann man dann statische Inhalte erneuern? Dafür kann man zum Beispiel das mod_rewrite-Modul des Apache Webservers und die Versionierung von SVN nutzen. Es bietet sich an, ein Skript zu schreiben, das in angegebenen Verzeichnissen nach definierten Inhalten wie Bild-, JS- und CSS-Dateien scannt. Von den gefunden Dateien wird dann die aktuelle Revision aus dem SVN abgefragt und in einer Konstante abgespeichert. Daraus ergibt sich eine Datei, die Konstanten mit den Versionsnamen der definierten Objekte enthält. Diese wiederum kann in die Anwendung eingebunden werden. Damit stehen die Konstanten zum Beispiel in einem Template oder einer View zur Verfügung. Ein Beispiel, wie es in einer View aussehen könnte:
//Konstante:
//View:
<link href="<a href="http://stat.example.com/css/http://stat.example.com/css/<?php echo(STATIC_CSS_MAIN);?>" rel="stylesheet" type="text/css" />
Mit dieser Konfiguration wird der angefragte Inhalt für lange Zeit gecacht. Sollte sich der Inhalt ändern, wird einfach eine neue Version ausgeliefert, die über die Rewrite Rule auf die richtige Datei geleitet wird. Das heißt, die Invalidierung wird aktiv über die Umbenennung des Inhaltsnamens (Versionsnummer) von der Applikation aus gesteuert. Die restlichen Anfragen sind somit Anfragen nach dynamischen Inhalten. Eine weitere Gruppierung dieser Inhalte nach ihrem Gültigkeitszeitraum ist sinnvoll, da ein Browser im Normalfall Inhalte so lange nicht vom Webserver abfragt, wie sie gültig sind. Das Setzen dieses Zeitraums geschieht über den Expire-Date-Header. Eine Applikation besteht aus dynamischen Inhalten, die sich oft, öfter, selten oder sehr selten verändern. Hiernach gilt es zu gruppieren. Das bedeutet für die Gruppierung der Inhalte, dass eine sinnvolle Zeit für den Expire-Date-Header für jede Gruppe definiert wird. Zusätzlich wird mit dem Last-Modified-Header eine zweite Schicht eingebaut, die zwar Anfragen stellt, aber kaum Serverlast verursachen sollte.

Caching mit Reverse Proxy (Webcache)

In der Serverarchitektur einer hochfrequentierten Anwendung kann ein Reverse Proxy, z. B. Squid oder Varnisch, zur Steigerung der Auslieferungsperformance verwendet werden. Ein Reverse Proxy hält Inhalte im Cache zur Auslieferung an einen Browser für eine definierte Zeitspanne vor. Wenn der angefragte Inhalt abgelaufen oder nicht im Cache verfügbar ist, wird die Anfrage an den Ursprungsserver weitergeleitet und die Antwort dann im Speicher des Proxies gehalten. Für den Browser ist dieses Vorgehen weitgehend transparent. Der Reverse Proxy reduziert sowohl die Netzwerklatenz zwischen Client und Server als auch die Serverlast und ermöglicht eine relativ einfache Skalierung der Applikation bei Lastspitzen. Ein Netz aus verteilten Reverse Proxies wird auch Content Delivery Network (CDN) genannt. Die CDN-Anbieter verteilen Reverse Proxies über das gesamte Internet (oder nur über bestimmte Teile, wie z. B. Länder) und ermöglichen so sehr schnelle Antwortzeiten und gute Skalierbarkeit für Webseiten.
Abb. 1: Die Grafik zeigt schematisch die Funktionsweise eines CDN.

Abb. 1: Die Grafik zeigt schematisch die Funktionsweise eines CDN.

  Reverse Proxy Caches funktionieren ähnlich wie Browser Caches, im einfachsten Fall über das Setzen von HTTP-Headern. Falls ein Header so gesetzt ist, dass ein Proxy nicht cachen soll, wird der Proxy auch nicht cachen. Zusätzlich zu den Headern gibt es eine Reihe von HTTP-Protokoll-Definitionen, die das Verhalten von Caches beeinflussen. So ist zum Beispiel definiert, dass bei der Verwendung von HTTPS eine Antwort auf eine Anfrage nie gecacht wird. Das Gleiche gilt auch für die Antworten auf Requests, die die Methoden POST und PUT verwenden. Requests unter Verwendung von GET müssen einige Bedingungen erfüllen, damit sie gecacht werden können. Bei der Verwendung der DELETE-Methode in einem Request soll der über den URI zu identifizierende Inhalt im Cache als alt (stale) markiert werden. Die Antwort auf den DELETE-Request soll nicht gecacht werden. PUT und DELETE werden zum Beispiel im Symfony-Umfeld verwendet. Sehr zu empfehlen ist an dieser Stelle der Artikel von Fabien Potencier über die Verwendung von DELETE. RFC 2616 beschreibt „fresh“ so: Ein Inhalt ist unverbraucht, wenn sein Alter (age) noch nicht die festgesetzte Zeit der Aktualität (freshness lifetime) überschritten hat. „Age“ ist definiert als Zeit, die verstrichen ist, seit dem der Inhalt vom Ursprungsserver gesendet oder das letzte Mal vom Ursprungsserver validiert wurde. „Freshness lifetime“ ist so angegeben: Das Zeitintervall zwischen der Herstellung eines Inhaltes und seiner angegeben Ablaufzeit. „Stale“ bedeutet für einen Inhalt, dass die Zeit der Aktualität überschritten wurde. Ein gecachter Inhalt wird (vom Cache) als unverbraucht (fresh) angesehen, wenn er ohne Nachfrage vom Cache zum Ursprungsserver direkt ausgeliefert werden kann. Wie kann man also die HTTP-Header so setzen, dass ein Reverse Proxy vernünftig cachen kann? Zuerst wird im Zusammenhang mit Caching zwischen zwei verschiedenen Header-Modellen unterschieden. Hier ist zum einen das „Ablauf-„(Expire)- und zum anderen das „Validierungs-“ (Validate-)Modell relevant. Das „Ablauf“-Modell beschreibt die korrekte Berechnung von Ablaufzeiten, die den Cache am effektivsten arbeiten lassen sollen: Anfragen an den Ursprungsserver werden weitestgehend vermieden. Der Expires-Header ist ein Basis-Header zum Setzen der Aktualität von statischen Inhalten. Wie im Abschnitt Browser-Cache ausgeführt, wird der Expires-Header genutzt, um statische Inhalte auf eine lange Gültigkeitsdauer zu setzen. Allerdings kann er auch genutzt werden, um Inhalte, die periodisch erneuert werden, wie zum Beispiel den News-Bereich einer Webseite oder auch nur die Liste der gerade angemeldeten User, für einen definierten Zeitraum im Cache zu behalten. Im Fall des News-Bereichs genau bis zum Erreichen des nächsten definierten Zeitpunkts einer Aktualisierung, für die Liste der gerade angemeldeten User evtl. nur 2 Minuten. Natürlich setzt dies voraus, dass die Liste z. B. über AJAX in ein größeres Template geladen wird oder aber, wie später in dem Abschnitt über ESI beschrieben, über den Proxy mit dem Template verbunden wird. Zwei wichtige Eigenschaften des Expire-Headers: Die Zeit wird immer in Greenwich Mean Time (GMT) angegeben und als einzige gültige Zeitangabe existiert das HTTP-Date-Format. Zum Beispiel:Expires: Fri, 30 Oct 2009 19:00:00 GMT. Hier erkennen wir eine Einschränkung des Expire-Headers. Der Einsatz setzt voraus, dass das Ursprungs- und Caching-System synchron sind. Das heisst, dass die Zeiten auf beiden Systemen identisch sind, ansonsten würde es zu falschen Annahmen über die Aktualität eines Inhalts kommen und dadurch geplante Änderungen (z. B. der tägliche geänderte News-Bereich) nicht zur geplanten Zeit aktiv werden. Zur Lösung dieses Problems wird empfohlen, alle beteiligten Systeme über das Network Time Protocol (NTP) (4) zu synchronisieren. Das „Validierung“-Modell beschreibt die Verwendung eines Inhalts in einem Cache nach der Validierung mit dem Ursprungsserver. Dafür muss immer ein Request aufgebaut werden. Zur Validierung gibt es so genannte Cache-Validator-Header. Einer dieser Header ist Last-Modified. Er beschreibt, wann der angefragte Inhalt das letzte Mal modifiziert wurde. Der andere ist der Entity-Tag-Header. Er beinhaltet einen Hash-Wert einer Seite, wie im Abschnitt zum Browser-Cache beschrieben. Beide Header sollten vom Ursprungsserver gesendet werden. Allerdings hat dies auch die schon im Abschnitt Browser-Cache genannten Nachteile. Eine Lösung ist die Verwendung des Entity Tags für Inhalte, die sich öfter ändern. Weitere wichtige HTTP-1.1-Header sind die Cache-Control-Header. Mit ihnen wird das Verhalten eines Caches gesteuert. Sie sind für HTTP-1.0-Caches nicht verfügbar. Stellt der Reverse Proxy fest, dass ein angefragter Inhalt nicht mehr aktuell ist, wird er als stale markiert und muss überprüft werden. Das heißt, der Reverse Proxy baut eine Verbindung zum Ursprungsserver auf und erfragt die Gültigkeit.

Parameter

Funktion

Cache-Control: max-age Definiert die maximale Zeit, die ein Inhalt als aktuell gilt. Allerdings ist die Angabe relativ zum Request in Sekunden.
Cache-Control: min-fresh Wird vom Browser gesendet und bedeutet dem Cache, eine Antwort nur dann zu liefern, wenn sie aktuell genug ist.
Cache-Control: s-max-age Funktioniert genau wie max-age, nur dass er nur für shared caches (zum Beispiel Proxies) gilt.
Cache-Control: public Markiert authentifizierte Antworten als cachebar, auch wenn diese normalerweise nicht gecacht würden.
Cache-Control: private Bedeutet, dass eine Antwort nicht von einem Shared Cache gespeichert werden darf, wohl aber vom Browser-Cache.
Cache-Control: no-cache Wird eingesetzt, um sicherzustellen, dass ein Request zur Validierung an den Ursprungsserver weitergereicht wird.
Cache-Control: no-store Bedeutet für eine Cache: Nicht cachen. Niemals.
Cache-Control: must-revalidate Wird vom Ursprungsserver gesendet und bedeutet für einen Cache, dass er die Anfragen nach diesem Inhalt immer vom Ursprungsserver validieren lassen muss.
Cache-Control: proxy-revalidate Bedeutet das Gleiche wie must-revalidate, allerdings nur für Shared Caches.
Cache-Control: stale-while-revalidate Wird eingesetzt, um hochfrequente Webseiten vor zu vielen Concurrent Requests zu schützen. Es ist nicht Teil des offiziellen RFCs, wird aber von den neueren Proxy Servern (Squid, Varnish) unterstützt.
Cache-Control: stale-while-error Mit diesem Header kann einem Proxy Cache beigebracht werden, im Fall eines 5xx einen alten Inhalt auszuliefern. Er ist auch nicht Teil des RFCs, wird aber ebenfalls von Squid und Varnish unterstützt.
Auf hochfrequentierten Webseiten ergibt sich hier ein Problem: Viele Anfragen werden bei einem stale auf einmal an den Ursprungsserver weitergeleitet. Das heißt, dass die Anzahl der Concurrent Requests schlagartig steigt. Natürlich sollte die Applikation so aufgebaut sein, dass auch diese Anfragen kein Problem darstellen, aber man kann auch über einen schönen Trick eine „BruteForce“-Attacke verhindern. Der Trick heißt: Stale-While-Revalidate und stellt sicher, dass vom Reverse Proxy so lange die alte Version einer Seite ausgeliefert wird, bis ein neuer Inhalt als aktuell im Cache des Proxies existiert. Dies hat natürlich den Nachteil, dass man sich abhängig von einem Proxy macht und kann im Fall eines Ausfalls dazu führen, dass die Webseite die Anfragen nicht mehr verarbeiten kann. Daher ist ein Proxy immer nur zur Performanceverbesserung einzusetzen, nicht aber zum Auffangen einer schlechten Applikationsarchitektur, die ohne ausgedehntes Caching nicht in der Lage ist, eine Webseite auszuliefern. Ein weiterer interessanter Ansatz zur Performanceverbesserung einer Applikation ist der Einsatz von Edge Side Includes (ESI). ESI ermöglicht die Verwendung von einzelnen dynamischen Elementen einer Site in einem statischen Haupttemplate. Diese Möglichkeit wird Inclusion genannt und ist zu vergleichen mit dem Nachladen von dynamischen Inhalten per AJAX aus einem gecachten Template heraus. ESI bietet enorme Möglichkeiten zur Performanceverbesserung einer Webseite, ist allerdings aufwändig in der Integration. Gerade in schon vorhandenen Projekten ist eine Menge Konzeptionsarbeit notwendig, um die Vorteile von ESI zu nutzen. Zur Nutzung von ESI wird die bestehende HTML-Repräsentation einer Webseite in Snippets eingeteilt. Wir definieren Snippet als einen eigenständigen Bereich einer Webseite. Dabei gibt es immer ein Haupt-Template, das die einzelnen Snippets referenziert. Es ist also der Container für verschiedene Snippets einer Webseite.
Abb. 2: Die blau hinterlegten Bereiche entsprechen jeweils einem Snippet.

Abb. 2: Die blau hinterlegten Bereiche entsprechen jeweils einem Snippet.

  Zur Vereinfachung der Verwendung eines Snippets sollte es von einem öffnenden und einem schließenden Div-Tag umschlossen werden. Es besitzt eine eineindeutige ID und stellt im Backend alle notwendigen Daten zur Darstellung zur Verfügung. Im Cake- und Symfony-Umfeld entspricht eine Component im Ansatz einem Snippet. Das Haupttemplate beinhaltet ESI-Elemente, die zum Beispiel einem Reverse Proxy, der ESI „versteht“ veranlassen, die referenzierten Snippets zu nachzuladen:
//arktikel.html:
<esi:include src="http://www.example.com/Sport/Ferrari/01/index.html" alt="http://www2.example.com/Sport/Ferrari/01/index.html" onerror="continue"/>
Das ESI-Include-Element beschreibt über src=““, welches Snippet nachgeladen werden soll. Das Attribut alt=““ definiert ein alternatives Snippet, das geladen werden soll, wenn das src=““ nicht geladen werden kann. Sollten beide nicht geladen werden können, gibt onerror=““ die Anweisung, was passieren soll. Continue bedeutet hier, dass das include einfach übergangen wird und das nächste ESI-Element im Template bearbeitet wird. Dieses Beispiel würde Sport/Ferrari/01/index.html von www.example.com laden und (je nach Header) speichern. Bei der nächsten Anfrage würde dann ggf. der gecachte Inhalt ausgeliefert. Ebenfalls interessant ist die Möglichkeit, über das ESI-Inline-Element bestimmte Bereiche einer HTML-Antwort-Seite zu markieren und zu cachen. Diese Bereiche stehen dann für andere HTML-Seiten als Inhalte zur Verfügung, falls fetchable=“yes“ gesetzt wurde:
    Michael Schumacher fährt jetzt doch nicht mehr für Ferrari.
ESI kann auch mit komplexen Abhängigkeiten umgehen, z. B.: Lade Inhalt A, wenn COOKIE A gesetzt ist und B, wenn COOKIE B gesetzt wird. Verschiedene Bedingungen können ebenfalls abgefragt und je nach Ergebnis kann darauf reagiert werden. Das Dokument unter [6] beschreibt die verschiedenen Verwendungsmöglichkeiten von ESI. Invalidierung von Webcaches kann aktiv über PURGE oder Umbenennung der Resource erfolgen. Passiv geschieht die Invalidierung über Expire oder CacheControl. Für ESI gibt es das ESI Invalidation Protocol.

Applikations-Caching

Bis jetzt haben wir nur Caches besprochen, die unabhängig von der Applikation wirken. Browser- und Webcache treten beide in Aktion, bevor die Anfrage den Webserver erreicht. Es wäre wunderbar, wenn wir mit diesen beiden Strategien unsere Anwendung schon so stabil bekommen, dass wir gar nicht weiter über Optimierungen nachdenken müssen. Leider klappt dies in den meisten Fällen nicht so einfach, denn irgendwann muss jede Seite einmal aufgebaut werden, bevor sie überhaupt gecacht werden kann. Wenn ein Cache noch kalt ist, er also keinerlei Daten zwischengespeichert hat, ist die Chance groß, dass die Applikation viele Berechnungen auf einmal machen muss. Damit nicht jeder Request, der es bis zum Webserver schafft, alle Berechnungen machen muss, gilt es auch hier zu cachen. Die Gesamtzahl der registrierten Benutzer muss zum Beispiel nur einmal aus der Datenbank geholt werden und nicht für jede Seite, die diese Information anzeigt. Das Prinzip ist also im Kern das gleiche wie bei den anderen Ansätzen. Daten, die von mehreren Anfragenden benötigt werden, sollten im Speicher vorliegen. Was zu speichern ist, ist klar: Berechnungen, die häufig gebraucht werden und welche, die sehr kostspielig sind. Die Herausforderung beim Applikations-Cache ist es vielmehr, den Ort des Zwischenspeichers zu bestimmen. Mögliche Orte sind hier der Hauptspeicher eines Servers oder die lokale Festplatte. Beide Ansätze haben Vor- und Nachteile. Die Hauptspeicherlösung, die meistens über memcached geschieht, hat den großen Vorteil, dass dank des verwendeten Protokolls auch Hauptspeicher von entfernten Maschinen verwendet werden können. Leider ist dieser Cache sehr beschränkt in seiner Größe und kann somit nicht sicherstellen, dass benötigte Daten auch zur Verfügung stehen, solange sie benötigt werden. Anwendungsgebiete wären hier zum Beispiel Daten, die für eine relativ kurze Zeit gültig sind und das Ergebnis nicht allzu großen Speicherbedarf benötigt. Entscheidet man sich für die lokale Festplatte, so kann man auf einen fast uneingeschränkt großen Zwischenspeicher zurückgreifen. Komplett gerenderte Seitenteile wären zum Beispiel ein Fall für die lokale Ablage. Großer Vorteil dieser Strategie ist Unabhängigkeit zu irgendwelchen PHP-Paketen. Jede noch so beschnittene moderne PHP-Version sollte in der Lage sein, lokal Dateien abzulegen. Auf den Hauptspeicher hingegen zuzugreifen benötigt Rechte, die vielleicht nicht jedem gegeben sind. Bei beiden Lösungen sollte man aber das Rad nicht neu erfinden und auf eine bereits existierende Lösung, z. B.Zend_Cache oder der memcached-Erweiterung, zurückgreifen.

Wie bringt man Caching in existierende Projekte?

Wir haben eingangs bereits erwähnt, dass man Caching bei der Entwicklung gut dosiert anwenden sollte. Aus diesem Grund wird es häufig vorkommen, dass Engpässe in der Software, die erst relativ spät auftreten, nachträglich mit einem Cache versehen werden müssen. Pauschal ist es schwer, eine konkrete Vorgehensweise zu definieren, wie man mit so einem Bottleneck umgehen muss. In einigen Fällen muss man hier tief in die Architektur eingreifen. Um die Last auf dem Webserver aber trotzdem zu reduzieren, empfiehlt es sich, die Techniken zu verwenden, die keine Änderungen im Sourcecode nach sich ziehen. Eine solche Technik wäre der Webcache, der einfach vor die Applikation gestellt wird. Stellt man das System so ein, dass jede Seite fünf Minuten gültig ist, so sollte schon ein großer Teil der Last vom Server genommen sein. Nachteil ist die langsamere Aktualisierung des Inhalts. Ein Kommentar, der einem Artikel angehängt wird, erscheint so zum Beispiel erst nach bis zu fünf Minuten. In den meisten Fällen sollte dies aber kein Problem sein. Hierzu muss man natürlich seine Webseite gut kennen. Habe ich viele Inhalte, die stabil sind? Dann habe ich eine gute Chance, mit Webcache meine komplette Applikation abzudecken. Blogs, News-Seiten und Seiten mit vielen redaktionellen Inhalten würden hierzu gehören. Foren zum Beispiel, die von ihrer Aktualität leben, müssen leider andere Wege gehen, da ist der nachträgliche Einbau eines Caches mit tieferen Eingriffen verbunden.

Fazit

Caching gehört sicherlich zu einem der interessantesten Techniken in der Webentwicklung. Seine Webseite unter hoher Last noch schnell reagieren zu sehen, ist wohl der Wunsch eines jeden Softwareentwicklers. Caches sind aber häufig mit Vorsicht zu genießen, denn schnell erhöhen sie die Komplexität einer Webseite immens. Einen Cache dort einzubauen, wo sich ein Engpass auftut, ist sicherlich immer der richtige Weg. Vorher Engpässe zu erraten und somit Teile ihrer Wartbarkeit zu berauben, ist in den meisten Fällen ein Schritt in die falsche Richtung. Es gibt viele Sorten von Caches, und wenn möglich, sollte man die vorziehen, die die meiste Last vom gesamten System nehmen. Unter Betracht der Caching-Pyramide wäre der erste Ansatzpunkt, alles Mögliche im Client zu hinterlegen, dann einen Webcache einzuschalten und als Letztes die Applikation selbst ihre Dienste tun zu lassen. Diese Reihenfolge kann natürlich je nach Webseite anders sein, bei Standardangeboten im Internet sollte diese jedoch meistens greifen. Caching kann komplex sein, muss es aber nicht. Viele der angesprochenen Lösungen können schon out-of-the-box ihre Effizienz beweisen. Trotzdem sollte man immer auch im Hinterkopf behalten, dass neue Hardware auch eine legitime Lösung sein kann, falls man die Entwicklungsgeschwindigkeit und Wartbarkeit über die Kosten der Infrastruktur stellt.
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -