PHP-Applikationen und Caching – II

Im letzten Beitrag dieser Serie zu verschiedenen Caching-Verfahren für PHP-Applikationen hatte ich bereits einige Verfahrensansätze theoretisch vorgestellt und deren Vor- und Nachteile diskutiert. Bevor ich in weiteren Beiträgen einzelne Verfahren betrachte, erscheint es mir in einem Linux-Blog sinnvoll, kurz auf die Voraussetzungen einer Linux-Testumgebung - im besonderen eines Apache2-Servers - einzugehen.

LAMP

Aus meiner Sicht ist zum Testen vpn PHP-Caching-Verfahren eine typische, lokal installierte LAMP-Umgebung mit phpMyAdmin völlig ausreichend. Man muss nicht unbedingt ein Netzwerk zur Verfügung haben. Unter der Rubrik "LAMP / Webentwicklung" habe ich bereits früher ein paar Beiträge zusammengestellt, die beschreiben, man sich die Komponenten einer LAMP-Umgebung unter Opensuse lokal auf einem Entwicklungs-PC oder einem Laptop einrichten kann. Die Grundaussagen der Artikel

http://linux-blog.anracom.com/category/php-und-mysql/apache/
http://linux-blog.anracom.com/2010/08/28/lokale-mysql-phpmyadmin-installation/

gelten im wesentlichen auch unter Opensuse 12.2.

Testapplikation

Ein PHP-Entwickler, der sich mit Caching befassen will, hat sicher auch die eine oder andere datenbankgestützte PHP-Applikation parat,

  • deren Einsatz den Server belastet und messbar Serverzeit kostet,
  • die Webseiten als Output liefert
  • und deren Datenbestand gepflegt werden kann.

Falls nicht, empfehle ich, sich eine einfache Applikation zu basteln, die eine Datenbanktabelle abfragt und das Listenergebnis schlicht in formatierter Form auf einer HTML-Seite ausdruckt. Durch einfach Loops mit "sinnlosen" aber zeitintensiven Operationen für unsere Tests erzeugt man Serverlast in einem spürbaren Bereich. Z.B. kann man ja die Datenbank-Tabelle Liste nicht nur einmal sondern 100 mal abfragen :-). Das reicht, um die Grundlagen zu studieren. Hat man keine eigene Applikation zur Datenbestandspflege, so greift man für gezielte Modifikationen einiger Datenbankeinträge eben auf phpMyAdmin zurück.

Auswertungstools am Browser

Bzgl. der Anzeige und des Auswertens von Caching-Informationen am Browser lohnt sich ein Blick auf die Entwicklertools von Firefox und im besonderen von Chromium/Chrome.

Die Chrome-Browser bringen für die Darstellung der Transferzeiten zum/vom Server, zur Cache-Nutzung und zu lokalen Aufbauzeiten für eine Webseite sehr schöne Grafik-Tools mit, die ich persönlich sehr schätze. Man erreicht die Tools über den Menüpunkt
"Tools  >>  Entwicklertools  >>  Network".

Im Firefox sollte man sich folgende AddOns installieren und sich auch mit der "Web-Konsole" vertraut machen:

  • Firebug - (akt. Vers.: 1.11.1) - hier erhält man auch grafische Tools zur Analyse der Transferzeiten von und zum Server
  • Web Developer - (akt. Vers.: 1.2.2) - ziemlich umfassende Toolpalette für Web-Entwickler, die per Menü oder Toolbar zugänglich ist
  • ggf. : Toggle Web Developer Toolbar 4 - ein Button zum An/Abschalten des Developer-Toolbars

Die AddOn-verwaltung findet sich im Linux-FF unter dem Menüpunkt "Extras  >>  Entwicklertools  >>  AddOns "

Vergleichsweise muss man die Wirkung des Cachings aber auch auf dem MS IE 8/9 nachvollziehen. Dazu nutzt man die sog. "Windows Internet Explorer Developer Tools" und im besonderen das "Network Capture"-Tool. Ich gehe auf deren Verwendung nicht genauer ein. Mehr Informationen erhält man aber unter

http://msdn.microsoft.com/de-de/library/ie/gg589512%28v=vs.85%29.aspx
und im besonderen unter
http://msdn.microsoft.com/de-de/library/ie/gg130952%28v=vs.85%29.aspx

Voraussetzungen für Experimente auf einem eigenen Apache 2-Server

Zur Grundeinrichtung eines Apache-Serves siehe
http://doc.opensuse.org/documentation/html/openSUSE/opensuse-reference/cha.apache2.html
http://thomas-huehn.de/web/caching-tutorial/#impl-server
oder
http://linux-blog.anracom.com/category/php-und-mysql/apache/

Welche Einstellungen man nach der Grundkonfiguration eines eigenen Apache2-Server eigentlich für Caching-Experimente verwenden? Einerseits müssen Header-Anweisungen für den Einsatz der verschiedenen Caching-Verfahren vom Web-Server für eine gegebene (virtuelle) Web-Domaine unterstützt werden. Andererseits muss der Server Anfragen von Clients mit Bezug zum Ablaufdatum von Seiten korrekt abhandeln und die richtigen Header-Antworten für die Clients generieren.

Auf einem Apache-Server müssen dazu bestimmte Module geladen sein, nämlich:

  • header
  • expires

Siehe hierzu:
http://httpd.apache.org/docs/2.2/mod/mod_headers.html
http://httpd.apache.org/docs/2.2/mod/mod_expires.html

Diese Module fügt man unter Opensuse entweder händisch in die Konfigurationsdatei "/etc/sysconfig/apache2" ein, oder benutzt YaST oder aber verwendet "a2enmod" auf der Kommandozeile zur Apache-Konfiguration. Zu "a2enmod" siehe die man-Seiten oder
http://www.mediamill.de/blog/2008/04/22/aktivieren-und-deaktivieren-von-apache2-modulen-a2enmod-a2dismod/
http://linuxpoison.blogspot.de/2010/07/how-to-enable-disable-modules-into.html
http://doc.opensuse.org/documentation/html/openSUSE/opensuse-reference/cha.apache2.html

Ferner muss (!) für die auf dem Server unterstützten virtuellen Domainen oder aber global angegeben werden, dass "Expiration"-Hinweise unterstützt werden sollen. Hierfür ist die Anweisung

ExpiresActive On

zuständig. Sie ist in den globalen Konfigurationsdateien des Apache-Servers und/oder in den Dateien/Konfigurationsabschnitten für die virtuellen Domainen zu setzen.

Dann findet sich ggf. noch das spezielle Thema eines lokalen Apache2 auf einem Entwicklungs-PC mit SSL. Wie und wo man ein Dummy-Zertifikat anlegt, steht unter
http://linux-blog.anracom.com/category/php-und-mysql/apache/.

Ich gebe zur Orientierung nachfolgend ein paar Auszüge aus relevanten Dateien für den lokalen Apache2-Server an, den ich auf meinem Opensuse 12.2-betriebenen Entwicklungs-Laptop mit mir rumschleppe:

Auszug "/etc/sysconfig/apache2":

APACHE_MODULES="actions alias auth_basic authn_file authz_host authz_groupfile authz_default authz_user autoindex cgi dir env expires include log_config mime negotiation setenvif ssl suexec status userdir asis imagemap php5 perl proxy proxy_ajp deflate headers rewrite proxy_balancer"
 
APACHE_SERVER_FLAGS="SSL"

Auszug listen.conf:

Listen 80
 
# RMO 443: SSL richtet sich nach den Suse Sysconfig-Variablen und dem entsprechenden Startup-Script
<IfDefine SSL>
  <IfDefine !NOSSL>
    <IfModule mod_ssl.c>
      Listen 443
    </IfModule>
  </IfDefine>
</IfDefine>
 
ExpiresActive   On

Die letzte Zeile wirkt global. Will man das nicht, muss man die Zeile weglassen.

Auszug aus "/etc/apache2/vhosts.d/vhost-ssl.conf":

<VirtualHost *:443>
 
  # General setup for the virtual host
    DocumentRoot "/srv/www/htdocs/Entwicklung/myproject/main/"
    ServerName myproject
    ServerAdmin admin@mymaindomain.com
    ErrorLog /var/log/apache2/error_log
    TransferLog /var/log/apache2/access_log
 
    ExpiresActive   On
 
    #   SSL Engine Switch:
    SSLEngine   on
 
 
    # SSL Cipher Suite:
    SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
    # Server Certificate:
    SSLCertificateFile /etc/apache2/ssl.crt/server.crt
    # Server Private Key:
    SSLCertificateKeyFile /etc/apache2/ssl.key/server.key
    # SSL Engine Options:
    #SSLOptions +FakeBasicAuth +ExportCertData +CompatEnvVars +StrictRequire
    <Files ~ "\.(cgi|shtml|phtml|php3?)$">
       SSLOptions +StdEnvVars
    </Files>
    <Directory "/srv/www/cgi-bin">
       SSLOptions +StdEnvVars
    </Directory>
    # SSL Protocol Adjustments:
      SetEnvIf User-Agent ".*MSIE.*" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0
    # Per-Server Logging:
    CustomLog /var/log/apache2/ssl_request_log ssl_combined
 
</VirtualHost>

Hier wird die für Caching-Tests wichtige Option "ExpiresActive On" explizit für die Test-Domaine namens "myproject" gesetzt.

Damit haben wir nun alle Voraussetzungen beieinander, um im nächsten Beitrag mit spezifischen PHP-Caching-Verfahren und zugehörigen Tests zu beginnen.

LSI/3ware – 3DM2-Update – Browser

Ich benutze schon seit langem LSI/3ware-Raid-Controller unter Opensuse. Dabei setze ich gerne das grafische Administrations-Tool "3DM2" für die Verwaltung der Controller-Einstellungen, evtl. Verify-Läufe etc. ein. Diese Tool ist eine Java-basierte Applikation für den Browser.

Seit etwa Ende 2009 gab/gibt es Probleme mit dem 3DM2-Tool in aktuellen Browsern wie Firefox, Chrome, Safari, MS IE9, Opera. Zumindest auf x86_64-Systemen. Das äußerte sich darin, dass der Standardaufruf

https://localhost:888

zu Fehlermeldungen wegen des selbst signierten Zertifikats führte und dass auch nach einem Akzeptieren des Zertifikats Netzwerkfehler hochkamen - vermutl. wegen eines falschen oder veralteten SSL-Algorithmus. Im Ergebnis wurde die 3ware-3DM2-Admin-Oberfläche nicht angezeigt.

Dies war auch unter der von LSI/3ware herausgegebenen "9.5.3_codeset"-SW-Paket-Version noch der Fall.

Ich hatte mir bis heute dadurch geholfen, dass ich für "3DM2" die Browser "Epiphany" oder "Konqueror" benutzte. Die erlaubten noch den Aufbau der Seiten. (Vielleicht weil hier laxer mit Sicherheitsanforderungen umgegangen wird ???)

Doch nun funktioniert die Sache mit einem aktuellen Codeset wieder. Heute habe ich mir jedenfalls das aktuelle "9.5.5_codeset"-SW-Paket von folgender Seite

http://www.lsi.com/channel/support/Pages/Download-Results.aspx?productcode=P00080&assettype=Software&component=Storage%20Component&productfamily=RAID%20Controllers&productname=3ware%209650SE-4LPML

heruntergeladen. Man bekommt dann ein ISO-Image-File auf die Platte. In meinem Fall auf ein aktuelles Opensuse 12.2 (x86_64)-System.

Das ISO-File mounted man dann (als root) direkt in Form eines "Loopback-Devices" (s. hierzu http://www.cyberciti.biz/tips/how-to-mount-iso-image-under-linux.html)

#   mount   -o   loop /extras/Updates/3ware/955_codeset/9.5.5.1-Codeset-Complete.iso   /mnt/

und startet dann mit

#   cd   /mnt/packages/installers/tools/linux
#   ./install.sh -i

die Installation. Man behält bei der entsprechenden Rückfrage am besten den Inhalt des Konfigurationsfiles unter
"/etc/3dm2/3dm2.conf" bei - sprich : man entscheidet sich für die Option, die das File unangetatstet lässt.

Nach dem Abschluss der Installation muss man den Service TDM2-Service neu starten.

# cd   /etc/init.d/
# ./tdm2   restart
redirecting to systemctl
redirecting to systemctl
Stopping 3ware DiskSwitch Daemon: done
redirecting to systemctl
Starting 3ware DiskSwitch daemon: done

Danach funktioniert der Aufruf von 3DM2 über https://localhost:888 auch unter Chromium/Chrome, Firefox und Opera wieder !

PHP-Applikationen und Caching – I

Einige Web-Hosting-Anbieter - u.a. 1&1 - haben mit ihren aktuellen Webhosting-Angeboten eine Politik verbunden, die die Performance eines angebotenen Web-Hosting-Leistungspakets von dessen Preis abhängig macht. Dies wirkt sich vor allem bei Web-Seiten aus, die dynamisch mittels PHP, Perl oder anderen serverbasierten Scriptsprachen auf der Basis von Datenbankdaten generiert werden.

Einige preisbewusste Kunden diskutieren deshalb immer häufiger mit uns, was man tun kann, um die Performance von serverbasierten Web-Applikationen zu verbessern. In unserem Fall sind dies vor allem PHP5-Applikationen. Wir meinen, dass in vielen Fällen ein systematischer und situationsgerechter Einsatz von verschiedenen "Caching"-Verfahren zu einer substanziellen Verbesserung der Situation führen kann.

Ich stelle deshalb in diesem und in nachfolgenden Artikeln einige Caching-Ansätze für PHP-Applikationen zusammen, die in unserer praktischen Entwicklungsarbeit eine Rolle spielen.

Im vorliegenden Teil I der Beitragsserie gehe ich zuerst auf generelle Faktoren ein, die die Performance von Web-Applikationen beeinflussen. Caching-Verfahren wirken sich auf verschiedene dieser Faktoren aus.

Ich beschreibe danach die Grund-Prinzipien einiger gängiger Caching-Verfahren für PHP-Anwendungen. Ziel ist es aufzuzeigen, wie man den Interaktionsaufwand mit dem Web-Server durch die Kombination dieser Cache-Methoden sukzessive reduzieren kann.

Zudem betrachte ich auch ein paar fachliche und technische Voraussetzungen für Caching. Dabei ergibt sich, dass und warum ein konservativer Ansatz für bestimmte Caching-Verfahren oder deren Kombination sich sowohl an zeitbasierten Regeln als auch an Merkmalen für Änderungen der Datenbestände des Servers orientieren sollte.

Dieser erste Teil ist als Einführung also am ehesten für Leute gedacht, die sich erstmalig mit der Thematik des Cachens im Zusammenhang mit PHP-Applikationen befassen.

In den nachfolgenden Teilen der Beitragsserie widme ich mich dann dem Coding einzelner, spezifischer Caching-Lösungen unter PHP.

Performance von PHP-basierten Webseiten aus Sicht des Nutzers

Der Betrachter von Webseiten servergestützter Webapplikationen begreift die Performance der Applikationen und ihrer einzelnen Seiten hauptsächlich unter dem Aspekt, wie lange es dauert, bis der Browser nach einer Benutzerinteraktion mit einer bereits dargestellten Seite das gewünschte Resultat - in der Regel eine Webseite oder Teile davon - darstellt. Diese "Antwortzeit" ist natürlich ganz entscheidend davon abhängig, ob überhaupt ein Datentransfer zum oder vom Server und ein Seitenaufbau auf dem Server stattfinden:

In vielen modernen Applikationen werden kleinere lokale Zwischenschritte am Browser von einer lokalen Javascript-/JQuery-Logik übernommen, die per DOM Seiteninhalte manipuliert. Oftmals werden auch nicht ganze Webseiten neu generiert, sondern per AJAX/DOM nur Teile einer geöffneten Seite mittels der Ergebnisse einer asynchronen Serveranfrage im Hintergrund modifiziert. Von der Interaktion der Webseite mit dem Server bekommt der Anwender am Browser dabei bei einem asynchronen Datenaustausch im Hintergrund nur bedingt etwas mit.

Kritisch mit der Performance wird es in der Regel immer dann,

wenn vor der nächsten Userinteraktion eine (synchrone) Interaktion mit dem Server stattfindet oder stattfinden muss. Hier addieren sich Datentransferzeiten zwischen Browser/Client und Web-/Datenbank-Servern zu den erforderlichen Zeiten, um das jeweilige Programm am Server durchzuführen.

Wann erfolgt ein solcher Neu-Aufbau von Webseiten durch den Server ? Eine typische, einfache, aber regelmäßig auftretende Situation während der Benutzerinteraktion hierfür ergibt sich ggf. bereits durch den Seitenwechsel eines Nutzers am Browser zu Web-Seiten, die PHP-basiert sind.

Nicht ist störender als beim Hin- und Herwechseln zwischen Webseiten als bei denjenigen Seiten, die am Server durch PHP-Programme generiert werden, unverhältnismäßig lang warten zu müssen ! Und das, obwohl sich ggf. seit dem letzten Besuch der PHP-basierten Seite vielleicht gar nichts an deren Inhalt geändert hat.

Serverbasierte Applikationen wie PHP-Applikationen trifft die Seiten-Neugenerierung am Server besonders, weil die meisten Browser den Output von serverbasierten Script-Programmen nicht ohne Zusatzmaßnahmen cachen oder dies nur gemäß einer eigenen Heuristik tun. Die Webserver der meisten Hoster sind zudem praktisch nie so eingestellt, dass für PHP-Seiten defaultmäßig ein Caching angewiesen wird. Um diesen Punkt muss man sich der Entwickler schon selbst kümmern !

Bei ein wenig Nachdenken wird man feststellen, dass Caching in Kombination mit "Ajax/PHP" oder gar "PHP/SOAP/REST/Webservices" ein komplexeres Thema ist als Caching im Falle des Abrufes ganzer PHP-Seiten. Wir klammern Ajax und Web-Services aus den Betrachtungen dieser Beitragsserie weitgehend aus.

Hingewiesen sei auch darauf, dass bestimmte Caching-Verfahren natürlich nicht nur in Bezug auf Browser sondern auch gegenüber anderen Clients (Apps, andere serverbasierte Applikationen) interessant sind. Ob und inwieweit der Client explizite HTTP-Header-Anweisungen zum Cachen tatsächlich in ähnlicher Form wie ein Browser nutzen kann, lassen wir mal dahingestellt.

Performance-limitierende Faktoren für serverbasierte Web-Applikationen und für dynamisch generierte Webseiten

Die Performance von Webseiten, deren Inhalte z.B. vollständig oder teilweise durch PHP-Programme auf dem Webserver generiert werden, wird durch eine Vielzahl von Faktoren beeinflusst. Zu nennen sind u.a.:

  1. Die Auslastung des Servers und dessen Puffer-Mechanismen
  2. die benötigte Zeit zum aktiven Laden der dynamisch eingesetzten Script-Module des Webservers,
  3. die Ladezeit von benötigten Include-Dateien am Web-Server,
  4. die benötigte Zeit für die Interpretation und Durchführung des Server-Skript-Codes,
  5. die Zeit für den Verbindungsaufbau vom Webserver zu Datenbankservern und die evtl. Nutzung eines Verbindungspoolings,
  6. die benötigte Zeit für Datenbanktransaktionen und die Zeit für Datentransfers zwischen Webserver und Datenbankserver und umgekehrt,
  7. das Caching-Verhalten des Web-Servers bzgl. der PHP-Module und bzgl. des Ladens der Include-Dateien,
  8. die Effizienz von Sessionspeichern und zugehörigen Technologien (RAM, Filesystem, Datenbank),
  9. das Caching von Provider- und Proxy-Servern,
  10. die Geschwindigkeit der Internetanbindung und Datentransferzeiten zwischen Browser und Webserver,
  11. das Caching am Browser selbst (Nutzung des Browser-Caches).

Die Performance-Politik von Providern beeinflusst praktisch alle Punkte bis auf die letzten zwei.

Wie stark ist nun die Abhängigkeit der Performance von unterschiedlichen Hosting-Paketen und deren Preis? Wir haben je nach Vertrag unserer Kunden Unterschiede in der Größenordnung bis zu einem Faktor 5 für das gleiche Programm unter verschiedenen Hosting-Paketen registriert:

Ein Programm, das in einem lokalen Netzwerk mit Gigabit-Netzwerk, nicht ausgelastetem Web- und Datenbankserver ca. 70 bis 80 Millisekunden (ohne das Nutzen von Caching-Mechanismen) benötigt, erforderte typischerweise ca. 120 - 220 Millisekunden unter besten Bedingungen beim Provider. Je nach Vertrag und auch Auslastung der Server des Providers kamen jedoch auch Zeiten deutlich oberhalb von 500 Millisekunden oder gar 1,5 Sekunden vor.

Bei preismäßig "günstigen" Verträgen bewegt man sich also u.U. in einem vom Kunden deutlich wahrnehmbaren Reaktionsbereich der Webserver-Leistung. Eine Optimierung von Web-Applikationen durch den Entwickler ist also angesagt, wenn der Kunde "preisgünstige" Angebote wahrnehmen will oder muss. Optimierung kann grundsätzlich an vielen Punkten ansetzen - doch nicht immer kann ein Entwickler auf einfache Weise spürbare Effekte erzielen:

Etliche der genannten Punkte sind partiell natürlich abhängig von dem, was der Provider an Netz- und Server-Technik nutzt und anbietet. Dennoch kann der Entwickler einiges tun:

Optimierungsmaßnahmen bzgl. des obigen Punktes 3) benötigen z.B. eine intelligente Klassen- und Programm-Politik auf der PHP-Seite. Unter besten Bedingungen kann das Laden von Klassenbibliothek-Files bei umfangreichen Systemen durchaus ein performance-limitierender Faktor sein. Hat man den Webserver selbst unter Kontrolle, so mag sich hier ein Griff zu Tools wie APC lohnen.

Unter Punkt 4 ist natürlich nicht nur eine performante Server-Hard- und Software gefragt. Selbstverständlich lassen sich auch PHP-Programme durch intelligente Maßnahmen des Entwicklers optimieren. Dies kann jedoch sehr aufwändig werden - und Kosten/Nutzen-Betrachtungen sind wichtig. Und welcher Entwickler geht schon ohne echte Not an die Grundfesten von Applikationen heran, die seit Jahren im Einsatz sind ?

Um eine optimale SQL-Performance muss sich der Entwickler zu einem essentiellen Anteil in jedem Falle selbst kümmern. Die Wahl geeigneter Tabellen-Varianten des RDBMS, die Wahl der Aufgabe angemessener Sperrmechanismen, Performance-Optimierung des Datenmodells, wo sinnvoll auch Einsatz von Denormalisierung und optimierte Indizierung sind hier erste Stichworte.

Man sollte sich als Kunde jedoch darüber im Klaren sein, dass der Aufwand für eine nachträgliche Performance-Optimierung von Web-Applikationen beträchtliche Ausmaße annehmen kann. Für die entsprechenden Kosten kann man sich dann ggf. auch ein besseres Hosting-Paket oder einen eigene Root-Server leisten.

Will man als Entwickler in der Kooperation mit dem Kunden schnell und effizient positive Effekte hinsichtlich der Performance erzielen, lohnt es sich daher durchaus, Caching-Methoden zu untersuchen und diese über klar abgegrenzte, überschaubare Funktionen oder Klassen-Methoden in die PHP-Applikationen zu implementieren. Der Aufwand dafür hält sich nämlich in Grenzen.

Ziele von Caching-Verfahren

Das grundlegende Ziel des Einsatzes von Caching-Verfahren macht sich an folgender Frage fest:

Wie kann ich die Interaktionsdauer mit dem Web-Server durch das Ausnutzen von Pufferspeichern - also Caches - und die Analyse von Änderungsinformationen zur Datenbasis der Webseiten so weit als möglich reduzieren ?

Strategien in diesem Zusammenhang können sein:

  • bereits erzielte Ergebnisse - sprich Webseiten - in direkt verwertbarer Form zwischenzuspeichern und aus entsprechenden Pufferspeichern zu laden, statt den Server zu einer Seitengenerierung aufzufordern,
  • den Kommunikationsaufwand und Datenaustausch zwischen Browsern und Servern auf das notwendige Maß zu reduzieren

und vor allem:

  • die Vermeidung von ressoucenintensiven Programmabläufen und Datenbanktransaktionen auf den Servern des Providers, soweit das unter dem Aspekt einer dynamischen Änderung der Datenbasis vertretbar ist.

Pufferspeicher - zeitbasierte Regeln - Änderung der Datenbasis von Webseiten

Um die eben genannten Ziele zu erreichen, ist es zum einen notwendig, Pufferspeicher und Caches zu nutzen. Hierfür kommen Browser-Caches, Server-Session-Speicher oder auch selbst-verwaltete "Datei-Caches" am Server in Betracht. Intelligentes Daten- und Datei-Caching kann PHP-seitig teilweise auch im PHP-Code-Ablauf realisiert werden. Aber natürlich muss man primär die "Public-" bzw. "Private-" Caches im Sinne von Proxy- und Browser-Caches für die generierten Webseiten und deren Elemente ausnutzen.

Neben der Notwendigkeit der Nutzung von "Pufferspeichern" ist es aber auch erforderlich, den Bedarf nach einer Aktualisierung von Webseiten und deren Inhalten

  • anhand von Regeln - z.B. für Zeitintervalle - zu steuern
  • und aufgrund der Analyse von definierten Änderungsmerkmalen der Datenbasis am Server festzustellen.

Beide Aspekte sind auszutarieren: Ein zu langes Zeitintervall für die Nutzung von Pufferspeichern steht ggf. in Konflikt mit dem Anspruch, aktuelle Informationen auch dann zu liefern, wenn sich die Datenbasis einer Webseite geändert hat.

Fachliche Voraussetzungen für Caching

Der Aspekt der Änderung der Datenbasis für den Inhalt einer Webseite oder generell von serverbasiertem Output führt uns zu den fachlichen Voraussetzungen für den Einsatz von Caching-Verfahren. Aus meiner Sicht gilt ganz allgemein:

Voraussetzung für den Einsatz von Caching-Verfahren ist, dass sich der Inhalt einer generierten Webseite aus Client- bzw. Kundensicht nicht laufend und zeitnah ändern muss, sondern über einen definierbaren Zeitraum konstant bleiben darf.
 
Auf der Client- oder Kundenseite darf kein Schaden entstehen, wenn eine Änderung erst nach einem definierten Zeitintervall am Client oder Browser bekannt wird.

Wie groß das zu wählende Zeitintervall ist, hängt natürlich vom Einsatzzweck der Website ab. Caching-Zeitintervalle können sich zwischen Sekunden und Wochen bewegen. Bei einem Newsticker wird das Zeitintervall konstanter Information natürlich viel kleiner sein als bei einer Informationsseite, deren Inhalt sich bekanntermaßen nur zu Beginn eines jeden Monats ändert.

Macht man sich jedoch klar, dass die typische Interaktionsdauer eines Kunden mit Web-Informationsseiten vielleicht zwischen 1 und 15 Minuten liegt, so wird klar, dass bereits das Ausnutzen von Caching-Zeiten im Bereich weniger Minuten bis zu einer Viertelstunde einen großen Effekt auf den subjektiven Performance-Eindruck eines Kunden haben können.

Wir erkennen hier übrigens, dass Ajax tatsächlich ein schwieriges Thema im Zshg. mit Caching ist:
Wann setze ich denn Ajax ein? Meist doch dann,

  • wenn eine Benutzereingabe zu einer direkten "Aktualisierung" von Datenbeständen auf dem Server und nachgelagert auch von bestimmten Inhalten der aktuell am Browser geöffneten Webseite führen soll (synchron oder asynchron). Caching ist hier in der Regel kontraproduktiv. Man steuert vielmehr aktionsgerecht selbst, was zwischen Server und Browser transferiert werden soll.
  • wenn eine Abfrage von listenartigen Vorschlägen aufgrund von Benutzereingaben gestartet wird. Das Problem ist hier, dass das Ergebnis solcher Abfragen noch während der Benutzereingabe Modifikationen erfahren soll - z.B. im Sinne von sich laufend modifzierenden Filtern. Hier hängt der Sinn eines möglichen Cachings vor allem davon ab, ob man die Filterung am Server oder am Client vornimmt und davon, wie vorhersagbar und wohldefiniert bestimmte Benutzerabfragen absehbar sind.
  • generell wenn Abfragen asynchron im Hintergund vorgenommen werden sollen, ohne die Webseite zu verlassen. Hier stellt sich die Frage, ob und wie das Ergebnis - oft ein XML-formatierter Datenstrom oder per JSON sequentialisierter Datenstrom - wo und wie gecached werden könnte.

Einige Caching-Verfahren

Kommen Browser- und Proxy-Caches zum Einsatz werden statische Webseiten ohne besondere Vorkehrungen nur einmal oder periodisch vom Server geladen, danach kommen ihre Inhalte für definierte Zeitintervalle aus dem Cache des Browsers oder eben eines Proxys. Bei dynamisch generierten Webseiten - wie PHP-Seiten - hängt die Caching-Politik vom Browserhersteller und von Einstellungen der Proxys wie des Webservers selbst ab - falls keine besonderen Vorkehrungen getroffen werden.

Gezieltes, anweisungsgesteuertes Caching im Cache des Browsers macht ggf. den Unterschied zwischen einigen zehn Millisekunden für den Seitenaufbau im Gegensatz zu einer Sekunde aus - pro Seite ! Das ist ziemlich durchschlagend. Unter bestimmten Bedingungen kann es aber auch interessant sein, Webseiten aus einem selbst verwalteten File-Cache aus dem Webserver zu ziehen.

In einer "HTTP 1.1"-Umgebung sind für normale PHP-Applikationen vor allem folgende vier Verfahren zur Ausnutzung von Caching-Mechanismen interessant, wobei alle Methoden explizit die Unterstützung des jeweiligen PHP-Programms (und des Webservers) erfordern. Wir ordnen die Verfahren nach dem Grad der Reduktion der Interaktion mit einem Web/Datenbank-Server und der Vermeidung von unnötigen Prozessen auf dem Server an :

  1. Methode 1 - Explizite Caching-Anweisung an "Public" und "Private" Caches mit einem limitierenden Zeitintervall:
    Der Browser oder ein chache-gestütztes Client-System erhalten den Hinweis, den Server über ein definiertes Zeitintervall hinweg nicht abzufragen, sondern explizit ihren Cache zu benutzen. Erst nach Ablauf des vorgegebenen Zeitintervalls wird der Server erneut kontaktiert: die geforderte Seite wird dann über das angesprochene PHP-Programm vollständig neu generiert (oder ggf. aus einem serverseitigen Filecache geladen) und auf dem aktuellen Stand zum Browser, in dessen Cache und die Caches von Proxy-Systemen etc. übertragen.
  2. Methode 2 - Check auf Änderungen von Webseiten und "Not Modified"-Rückmeldung im HTTP-Header an den Browser:
    Die auf dem Server angesprochenen PHP-Programme überprüfen anhand bestimmter Merkmale als allererstes, ob sich die Datenbasis der dynamisch zu generierenden Website seit der letzten Serveranfrage geändert hat. Ist dies nicht der Fall, erhalten der Browser und andere Cache-Systeme per HTTP-Header einen expliziten Hinweis (Code 304-Meldung), ihren Inhalt weiter zu verwenden, aber bei der nächsten Seitenabfrage den Server erneut bzgl. möglicher Änderungen abzufragen. Nur falls in der Zwischenzeit Änderungen aufgetreten waren, wird der geänderte Webseitencode entweder neu aus Datenbankinhalten erzeugt oder aus einem intermediären Datei-Puffer (s. Punkt 4) des Webserver geladen und zum Client übertragen.
  3. Methode 3 - Kombination der Methoden 1 und 2:
    Methode 3 kombiniert die erste und die zweite Methode: Grundsätzlich wird ein zeitlimitiertes Caching zugelassen. Nach Ablauf des Zeitintervalls überprüft das angesprochen PHP-Programm aber zunächst, ob sich die Datenbasis seit der letzten Seitenauslieferung überhaupt geändert hat. Wenn nicht, wird erneut ein clientseitiges Caching für ein definiertes Zeritintervall erlaubt - wobei die Seite nicht (!) neu generiert oder geladen wird. Im Falle einer Änderung der Datenbasis wird die Seite dagegen neu auf dem Server erzeugt und in die Caches übertragen - das Caching-Zeitintervall auf der Kunden- oder Proxy-Seite beginnt erneut.
  4. Methode 4 - PHP-gesteuertes File-Caching am Server:
    Wohldefinierte Ereignisse auf dem Server werden genutzt, um den HTML/XHTML/XML-Code der benötigten Webseiten oder Informationen auf der Basis von erkannten Änderungen der Datenbasis neu zu generieren. Der durch das PHP-Programm generierte HTML/XHTML- oder auch XML-Code wird dann für ein definiertes Zeitintervall oder aber bis zum nächsten wohldefinierten Änderungs-Event als "Datei" auf dem Server in einem selbstverwalteten "Cache"-Verzeichnis abgelegt. Bei der nächsten Browserabfrage wird vom PHP-Programm direkt der Datei-Inhalt aus dem Verzeichnis-Cache geladen und zum Browser übertragen, falls keine sonstige Änderung der Datenbasis erfolgt ist. Ressourcenintensive Prozesse zur Webseitengenerierung werden so nur periodisch oder aber aufgrund definierter Änderungsereignisse fällig.
  5. Methode 5 - Kombination der Methoden 3 und 4:
    Grundsätzlich wird ein zeitlimitiertes Caching zugelassen. Nach Ablauf des Zeitintervalls überprüft das angesprochen PHP-Programm aber zunächst, ob sich die Datenbasis geändert hat. Ist ein aktuelles HTML/XHTML/XML-File zur geänderten Datenbasis vorhanden, wird dieses gezogen. Ist das nicht der Fall, wird die Seite neu generiert, en passant am Server als File abgelegt und zudem zum Client übertragen.

Einschränkungen, Vor- und Nachteile der Verfahren

Die einzelnen Verfahren weisen Vor- und nachteiel auf, die ich nachfolgend kurz diskutiere:

Einschränkungen für den Einsatz der ersten Methode - Caching während eines definierten Zeitintervalls

Beim Einsatz der ersten Methode, versucht man, eine Interaktion mit dem Server über definierte Zeitintervalle hinweg zu vermeiden. Diese Methode hat den Vorteil eines schnellen Seitenaufbaus auf der Basis lokaler Informationen und Objekte im Cache des Browsers. Das wirkt sich am Browser vor allem beim Wechsel zu und zwischen (eigentlich PHP-basierten) Webseiten sehr vorteilhaft auf die Performance aus. Durch die Nutzung des Browser-Caches wird nicht zuletzt auch der Server spürbar entlastet.

Der Nachteil der Methode 1 besteht jedoch darin, dass während des vorgegebenen Zeitintervalls eben auch keinerlei Serverinteraktion mehr stattfindet (soweit der Browser/Proxy die Caching-Vorgaben des Servers respektiert). Das cachende System - i.d.R. der Browser - erfährt (bei normaler Bedienung) innerhalb des vorgegebenen Zeitintervalls nichts (!) mehr über eventuelle Änderungen der Datenbasis des Webseiten-Inhalts. Die aus dem Cache gezogene Information ist also möglicherweise schon veraltet. Und selbst wenn der Browser die Seite neu anfordern würde, kämen ggf. veraltete Inhalte aus Proxy-Caches zurück.

Eine kluge, dem Einsatzzweck der Web-Applikation angemessene Vorgabe des Caching-Zeitintervalls an beteiligte Systeme ist also eine wichtige Voraussetzung der Anwendung der Methode 1.

Die zweite Voraussetzung für den Einsatz dieser Methode ist die, dass die Benutzerinteraktion nicht selbst zu einer Datenbestandsänderung führt und dass für den Aufbau der geforderten Webseite nicht unmittelbar eine komplette Neukalkulation und Neu-Generierung der Seite auf dem Server erforderlich ist. (Gezielt gesteuerte Ajax-Prozesse können den Cache natürlich immer umgehen. Ich lasse das aber mal außer Acht).

Man erkennt deshalb,

dass diese erste Methode des Cachings  nicht  für Previews und Voransichten von Webseiten geeignet ist , die mit Hilfe von CM-Systemen oder anderen Pflegemodulen aus Datenbankinhalten generiert werden.

Will man den Effekt von Datenbankänderungen sofort sehen, muss das Caching der zugehörigen Webseiten durch bestimmte PHP-Übergabe-Parameter, die bei den normalen Web-Adressen nicht auftauchen, umgangen bzw. explizit abgestellt werden.

Dies ist jedoch leicht realisierbar, da sich das Browser-Caching immer auf eine vollständige URL-Adresse - also inkl. aller GET-Parametern - bezieht. Bei der Übergabe von POST-Parametern muss eben ein bestimmter Zusatz-Parameter zur Situationsanalyse eingesetzt werden und die Namen für die Previewer-Programme sollten sich dann auch von denen der normalen Web-Generator-Programme unterscheiden.

Vor-und Nachteile der zweiten Methode - Prüfung der Änderung der Datenbasis vor einer Neugenerierung oder Cache-Nutzung

Die zweite Methode für sich erspart einem nicht eine Kommunikation zum und vom Server. Aber sie stellt ein Schlüsselelement dar, um den Aufwand auf dem Server zu begrenzen:

Sie erspart u.U. die Durchführung ressourcenintensiver PHP-Programmschritte und von RDBMS-Prozessen auf dem Web- und den Datenbank-Servern.

Die Latenz der PHP-Applikation hängt dann hauptsächlich von der Internetanbindung und im wesentlichen einer Datei- oder Datenbankabfrage auf Änderungen ab. Beides kann aber erheblich (!) schneller sein, als eine vollständige Neugenerierung der Seiteninformation durch komplexe Programme und Datenbanktransaktionen - wenn sich der Datenbestand nicht geändert haben sollte.

Der Einsatz der zweiten Methode ist aus meiner Sicht grundsätzlich sinnvoll. Hier muss man sich allerdings überlegen, wie und was man auf Änderungen prüft. In meinen Programmen sind das

  • erstens Datenbankänderungen
  • zweitens Änderungen von Files
  • drittens ggf. Änderungen von speziellen Files für die Caching-Methode 4
  • viertens Änderungen der Inhalte von Session-Speichern

Änderungen an anderen Datenbestände, die hier nicht genannt wurden, sind denkbar.

Datenbankänderungen bekommt man grundsätzlich über die Analyse von Timestamps in Tabellen mit. Hierbei muss man aber die Zusammenhänge der Daten - sprich das Datenmodell beachten ! Das Ergebnis des PHP-Programms wird ja i.d.R. von einer Kombination von Tabellen abhängen. Man muss sich also darüber klar werden, welche Timestamps eigentlich für den gewünschten Output relevant sind. Ggf. lohnt es sich, für die einfache Abfrage relevanter Änderungen im Rahmen von Caching-Verfahren spezielle Einträge in selbst verwalteten Timestamp-Tabellen explizit zu setzen oder z.B. über Datenbank-Trigger setzen zu lassen.

Es zeigt sich also, dass die Möglichkeit zum einfachen Erkennen der Änderungen von Datenbeständen für ein späteres Caching schon frühzeitig beim Design einer Applikation berücksichtigt werden muss.

Analoge Argumente gelten für andere Datenbestände, die in Kombination zur Erzeugung eines aktuellen Seiteninhalts wichtig sein mögen.

Ein weiterer Nachteil dieser Methode besteht übrigens ggf. darin, dass man die Verbindung zur Datenbank in einem PHP-Programm sehr frühzeitig aufbauen muss. Dies kann vor allem in sicherheitsrelevanten Applikationen eine Rolle spielen.

Vorteile der dritten Methode - Kombination aus zeitgesteuertem Caching und Prüfung der Datenbasis

Sind die Voraussetzungen für den Einsatz der Methoden 1 und 2 gegeben, bringt deren Kombination aus meiner Sicht nur Vorteile mit sich. Man cached lokal solange, wie das eben vertretbar ist. Erst danach wendet sich der Client/Browser wieder an den Server. Aber dort wird nur wirklich etwas getan, wenn dies aufgrund eingetretener Änderungen notwendig ist. Ansonsten wird erneut auf den am Client vorhanden Cache-Inhalt zurückgegriffen. Besser kann es kaum kommen - deswegen setze ich diese Kombination auch in vielen Applikationen ein.

Vor-und Nachteile der vierten und fünften Methode - Einsatz eines selbstverwalteten File-Cache für PHP-Output

Grundsätzlich ist es gut, einen selbstverwalteten File-Cache zu realisieren. Sein Einsatz lohnt sich vor allem unter der Voraussetzung, dass sich die Generierung der Webseite zeitlich und logisch weitgehend von der konkreten Nutzer- und Abfragesituation am Client abkoppeln lässt:

  • Eine durch Ereignisse oder Pflegepersonen ausgelöste Datenbestandsänderung führt für sich alleine und unabhängig von der aktuellen Benutzerinteraktion bereits zu einem wohldefinierten Aufbau der betroffenen Webseiten. Diese können im Prinzip also unmittelbar nach der Datenänderung als Files am Server erzeugt werden.
  • Der Inhalt der Seiten ist relativ unabhängig von dynamischen Parameterkonstellationen, die situationsspezifisch erst durch den Anwender der Web-Applikation definiert werden.

Sind diese Voraussetzungen gegeben, so gilt zusätzlich Folgendes:

Sollen oder müssen die generierten Files im selbst verwalteten Cache-Verzeichnis alle möglichen Abfragesituationen abdecken, so erkennt an schnell ein potentielles Mengenproblem. Bei komplexen Applikationen mit großen Datenbeständen kann dies durchaus ein Thema werden. Man muss sich also genau überlegen, welchen Output von PHP-Applikationen man in Puffer-Files überführen will. Für den einen oder anderen mag zudem der technisch erforderliche Einsatz des sog. Output-Bufferings unter PHP5 zur Erzeugung der Files ein unbekanntes Terrain darstellen.

Ein weiteres mögliches Problem ist, dass man den eigenen File-Cache natürlich auch im Rahmen seiner Applikationen selbst verwalten muss. Wie bei jedem anderen Cache wird man hier Zeitregeln und eine Mengenbegrenzung einführen. Dies führt dann zu FIFO-Mechanismen oder ähnlichem.

Insgesamt lohnt sich der Einsatz von selbstverwalteten Filecaches vor allem dann, wenn

  • die Generierung des HTML/XHTML/XML-Outputs substanzielle Ressourcen beansprucht,
  • die Änderung der Datenbasis von der aktuellen Benutzerinteraktion weitgehend entkoppelt ist,
  • die im File hinterlegte Information eine langfristige Gültigkeit hat.

Solche Voraussetzungen sind oft in CM- oder Pflege-Systemen gegeben, in denen Seiten erzeugt werden, deren Inhalt nicht dynamisch durch viele Parameter gesteuert wird und über relativ lange Zeitintervalle hinweg konstant bleibt.

Ein Beispiel hierfür wäre etwa eine Webseite, die einen CV darstellt. Der HTML-Code der Webseite kann unmittelbar nach Änderung der Datenbankinhalte zum CV erzeugt werden und bleibt dann lange konstant, also statisch. Hier lohnt sich die Ablage in Dateiform unmittelbar nach Durchführung der Datenänderung. Das PHP-Programm, das für eine Anfrage des Kunden zu dieser CV-Webseite verantwortlich ist, prüft auf Änderungen der Datei durch andere Pflegeprogramme ab und schickt den vorab generierten und hinterlegten Datei-Inhalt direkt weiter, wenn keine Änderungen vorliegen.

Ein Gegenbeispiel wäre etwa eine Projekt- oder Produktliste, auf die der Anwender interaktiv viele verschiedene Filter anwenden kann und deren Inhalt sich aufgrund äußerer Umstände in kurzen, unperiodischen Zeitabständen ändert. Gegenbeispeile stellen auch Seiten zu sehr vielen unterschiedlichen Items dar, die sich erratisch ändern.

Genannt sei ein weiterer Einsatzbereich des vierten Verfahrens - nämlich Ajax-Transaktionen: Es gibt bestimmte Ajax-Abfragen (z.B. die Generierung von relativ statischen Listen), deren Ergebnis sich nicht immer auf dem alleraktuellsten Status befinden muss. Dann lohnt es sich durchaus, XML- oder JSON-Daten nicht immer neu erzeugen zu lassen, sondern direkt Cache-Files zu entnehmen.

Insgesamt meine ich, dass man vor dem Einsatz der Methoden 4 und 5 zunächst Methode 3 ausprobieren sollte. Methode 3 ist - wie wir sehen werden - auch einfacher zu implementieren als ein selbst verwalteter File-Cache. Natürlich kann man die Methode 4 auch mit der Methode 3 kombinieren:

Wenn Änderungen am Datenbestand festgestellt werden, kann man zunächst überprüfen, ob seit der letzten Änderung bereits ein neueres File erzeugt wurde und dessen Inhalt dann als Output an den Browser weiterreichen. Ist kein hinreichend junges File gegeben, so kann man ein solches "en passant" anlegen.

Server-Entlastung

Caching ist auch interessant und wichtig für Leute, die ihre eigenen Webserver betreiben. Es liegt nach dem bisher Gesagten auf der Hand, dass ein vernünftiges Caching auf Basis der oben genannten Methoden insgesamt die Auslastung der Web-Server reduziert. Alle genannten Verfahren haben ja gerade zum Ziel, die Interaktion mit dem Server zu verringern. Im zeitlichen Mittel bleiben bei Einsatz von Caching-Verfahren mehr Serverressourcen übrig.

Nun haben wir das notwendige Rüstzeug beieinander, um uns den einzelnen Methoden zuzuwenden. Dies geschieht in einem weiteren Beitrag dieser Reihe unter der Rubrick "Lamp / Webentwicklung". Bis dann !