Nicht bei jedem Drupal Projekt sind die gleichen Performance Optimierungen durchzuführen. Um herauszufinden, wo bei einem bestimmten Projekt der Schuh drückt, kommt man um eine Analyse nicht herum. Dies sollte immer der erste Schritt einer Optimierung sein:
Analyse des Projektes
Um herauszufinden an welcher Stelle eine Optimierung sinnvoll ist, verschafft man sich mit einer Projekt- und Systemanalyse einen ersten Überblick. Durch Virtualisierung (wie VMware, XEN oder KVM) kann man sich dazu die Produktivumgebung “nachbauen”, um so nicht den normalen Betrieb des Projektes zu beeinträchtigen. Um ein Projekt unter Lastverhalten zu testen kann man ab (Apache Benchmark) oder jmeter einsetzen. So bekommt man heraus, auf welchen Seiten/URLs des Projektes besonders viel Last erzeugt.
Will man auf einer bestimmten Seite wissen, in welcher Funktion oder welchem Modul das Problem liegt, hilft ein Profiler weiter (wie z.B. XDebug).
Auch ist ein Blick in den Log-Files immer sehr aufschlussreich. Fehler in den Scripten verursachen meist nicht nur eine Fehlfunktion der Anwendung, sondern auch viel Serverlast. Besonders interessante Logfiles für ein Drupal Projekt sind die PHP, MySQL und Apache Error Log-Dateien, und natürlich auch die Watchdog-Einträge in der Drupal Datenbank. Weiterhin kann man MySQL so konfigurieren, dass Queries die eine definierte Anzahl von Sekunden überschreiben, in eine Log-Datei geschrieben werden (Slow Query Log). In der selben Datei kann man auch aufzeichnen lassen, wenn ein Index einer Tabelle fehlt. Aber Vorsicht beim aktivieren dieser Mechanismen auf dem Live-System, da dadurch mehr Last erzeugt wird!
Quick Wins - Optimierungen, die immer einsetzbar sind
- PHP OP-Code Caching (mit APC, Xcache, eaccelerator oder Zend Server)
- MySQL Query Cache
- die PHP Erweiterung mysqli der mysql Erweiterung vorziehen
- AllowOverwrite = none, abschalten der .htaccess Unterstützung des Webservers, um dadurch die Dateisystemzugriffe einzusparen. Die notwendigen Einstellungen für Drupal in der .htaccess Datei müssen dadurch in der vhost Datei gesetzt werden.
Beispiele häufig anzutreffender Szenarien
- Komplexe Funktionalität (z.B. große Anzahl an Modulen, sehr komplexe Module)
- Große Anzahl an gleichzeitigen Zugriffen von anonymen Benutzern (Gästen)
- Große Anzahl an gleichzeitigen Zugriffen von angemeldeten Benutzern
- Große Menge an Daten (z.B. viele Nodes oder Benutzer)
Komplexe Funktionalität
- Anzahl der aktiven Module reduzieren
- Refactoring der komplexen (eigenen) Module. Ein Profiler kann hier hilfreich sein.
- Austausch von Modulen mit performanteren Modulen, falls möglich
- falls eine Aufgabe nicht Realtime erledigt werden muss (also in dem Augenblick, in dem der Benutzer klickt), sollte man diese asyncron ueber einen Cronjob ausfuehren. Hier kann der Drupal hook_cron verwendet werden oder noch besser der Server cronjob.
- wenn die vorherigen Punkte nicht ausreichen: Caching Viele greifen gleich zu dieser Maßnahme, was allerdings ein Fehler ist. Es ist immer besser das Performance-Problem an der Stelle zu optimieren, an der es entsteht - und nicht im nach hinein über einen Cache. Hier sind zwei Bereiche, die sich anwenden lassen: 1. Funktionalität cachen, die noch nicht gecacht wird - vor allem in eigenen Modulen. Oder auch ganze Ausgabebereiche cachen, wenn der User-Cache dies zulässt. Drupal macht Cachen sehr einfach. Will man etwas im Cache ablegen, verwendet man die Funktion cache_set. Will man auf einen abgelegten Inhalt im Cache zurückgreifen, verwendet man die Funktion cache_get. Wo der Drupal Cache abgelegt wird, muss vom Entwickler nicht angegeben werden. 2. Drupal Cache legt standardmäßig den Cache-Inhalt in der Datenbank ab. Je nach Use-Case kann aber das Dateisystem oder meist der Hauptspeicher performanter sein. Für die Nutzung anderen Cache Engines kann man das Drupal Modul memcache oder cacherouter (für APC, eAccelerator, memcache, XCache) einsetzen.
Große Anzahl an gleichzeitigen Zugriffen von anonymen Benutzern
Was hier sehr positiv ist, dass normalerweise jeder anonyme Benutzer den gleichen Inhalt sieht (auch hier gibt es Ausnahmen). Dadurch kann man anwendungsunabhängige Techniken wie z.B. einen Reverse Proxy (Squid, Varnish) einsetzen. Das Prinzip ist einfach: der Proxy Server wird vor dem Webserver geschaltet. Hat der Proxyserver eine gültige Kopie der angefragten Seite, liefert er den Inhalt an den Client zurück. Falls nicht, leitet er die Anfrage an den Webserver weiter.
Es gibt auch eine Drupal Lösung über das Boost Cache Modul. Boost Cache legt eine statische Seite auf dem Dateisystem ab. Mit Hilfe von Webserver Rewrite Regeln wird auf diese Dateien umgeleitet, falls keine statische Datei existiert, wird als Fallback sozusagen, ganz normal das PHP Script ausgefuehrt. Wenn eine Regel greift bedeutet das, dass keine Datenbankverbindung aufgebaut werden muss, nicht einmal das PHP Script ausgeführt wird. Dies stellt einen enormen Geschindigkeitsschub dar.
Große Anzahl an gleichzeitigen Zugriffen von angemeldeten Benutzern
Bei diesen Szenario ist das Cachen der Seiten wesentlich komplexer, da eine Benutzergruppe (Role) oder auch einzelne Benutzer unterschiedliche Ausgabeseiten erfordern kann. Eine Möglichkeit ist Drupal Block Cache einzusetzen, um bestimmte Blöcke einen festen Zeitintervall zu Cachen. Das Modul authcache ermöglicht es auf Rollen oder Benutzerebenen zu cachen. ESI (Edge Side Include) ermöglicht es bestimmte Bereiche einer Seite zu cachen. Die Integration in Drupal ist aber nicht trivial und muss zu einem frühen Zeitpunkt in den Projektentwicklungsprozess aufgenommen werden.
Auf jeden Fall sollte man für dieses Szenario dafür sorgen, dass statischer Inhalt von einen Proxy oder einen CDN (Content Delivery Network) ausgeliefert wird, um die Anfragen an den Webserver oder Webserver-Cluster zu reduzieren. Ein CDN hat bei internationalen Projekten den enormen Vorteil, dass der Inhalt von Webservern ausgeliefert wird, welcher sich in der Nähe des Clients befindet. Beispiel: im CDN Netzwerk gibt es einen Server in Amerika, einen in Afrika und einen in Europa. Ruft nun jemand aus Amerika eine Seite aus dem Projekt auf, wird der statische Inhalt vom Server aus Amerika ausgeliefert. Natürlich ist ein CDN auch in anderen Szenarien immer eine gute Idee! Da man meist nicht überall in der Welt Server herumstehen hat, wird man in den meisten Fällen zu einem CDN Provider greifen müssen (die nicht ganz billig sind - aber die finanzielle Frage ist hier relativ, da viel Traffic sehr wahrscheinlich auch mit mehr Umsatz verbunden werden kann).
Falls nur ein Server verwendet wird, sieht das ganz so aus:
Will man nun unterschiedlichen Ports im Quellcode verwenden, kann man mit den folgenden iptables Anweisungen mit 2 vhosts arbeiten:
# Redirect lighttpd
# enable ip forward
echo 1 > /proc/sys/net/ipv4/ip_forward
/sbin/iptables --flush
/sbin/iptables -t nat --flush
/sbin/iptables -t mangle --flush
/sbin/iptables --policy INPUT ACCEPT
/sbin/iptables --policy OUTPUT ACCEPT
/sbin/iptables --policy FORWARD ACCEPT
/sbin/iptables -t nat --policy PREROUTING ACCEPT
/sbin/iptables -t nat --policy OUTPUT ACCEPT
/sbin/iptables -t nat --policy POSTROUTING ACCEPT
/sbin/iptables -t mangle --policy PREROUTING ACCEPT
/sbin/iptables -t mangle --policy OUTPUT ACCEPT
/sbin/iptables -t nat -A PREROUTING -i eth0 -p tcp -d 85.25.178.115 --dport 80 -j REDIRECT --to-port 81
# close port 81 from outside
/sbin/iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp -d 85.25.178.115 --dport 81 --syn -j DROP
Große Menge an Daten
Die Datenbankarchitektur von Drupal lässt auf der Anwendungsebene nicht viele Möglichkeiten eine Optimierung vorzunehmen, wenn es um große Datenmengen geht. Deshalb muss die Optimierung serverseitig erfolgen:
- Datenbank Partitionierung Ab MySQL 5.1 bietet MySQL eine anwendungstransparente Tabellenpartitionierung an. Die Schwierigkeit hierbei liegt bei der Erstellung des für den bestimmten Use-Case passende Definition der Partitionstabelle.
- Den Einsatz von Datenbank Replikation oder einen Cluster.
Das Projekt wird größer...
Um so größer das Projekt wird, um so wichtiger ist die Skalierung des Projektes:
- der Einsatz von Clustern: Datenbank, Webserver, CDN, Suche....
- auf die Drupal Suche verzichten und diese mit einer externen Suche ersetzen: z.B. mit Solr oder auch Google
- der Einsatz von CDN
- Reverse Proxies
- Externe Anwendungen die auf bestimmte Use-Cases spezialisiert sind wie Forum, Videos, Ad Server, u.s.w.
Pressflow
Wenn Skalierung in einem Projekt schon eine Rolle spielt, sollte man überlegen ob der Einsatz des Drupal Derivat Pressflow in Frage kommt. Drupal 6 ist nicht für den Einsatz von Reverse Proxies ausgelegt (Header wird falsch gesetzt), was einen Patch erfordert. Datenbank-Replikation wird auch nicht von Drupal6 unterstützt und erfordert auch einen Patch. All diese Patches sind in Pressflow enthalten und weitere PHP5 und MySQL Optimierungen. Mit Pressflow lassen sich alle contributed Module weiterhin einsetzen, auch die Umstellung von Drupal6 nach Pressflow ist unproblematisch (sofern man nicht selbst Hand am Core angelegt hat, was man ja bekanntlich vermeiden soll, wenn man sich selbst einen Gefallen tun will).
Sometimes you have to kill some kittens.
Nachwort
Performance Optimierung lässt sich nicht allgemein beschreiben, es kommt immer auf das individuelle Szenario an. Daher ist eine vorhergehende Analyse sehr wichtig, um die richtigen Schritte unternehmen zu können. Drupal bietet viele Optimierungsmöglichkeiten durch den modularen Aufbau. Viele Lösungen kann man einfach über ein contributed Modul einsetzen, andere Lösungen kann man mit Drupal leicht koppeln.