Manchmal verwundert es, warum sich die eine oder andere Web-Anwendung oder -Seite so langsam, geradezu zäh “anfühlt”. Für Kataloge gilt das gleiche.

Welche Gründe das haben – und wie man mit wenig Aufwand die gefühlte Performance für den Endanwender verbessern kann, hat Steve Souders für die Platform Engineering Group von Yahoo bereits vor einigen Jahren bei den Top 10 Web-Sites in den USA untersucht und in seinem Buch “High Performance Web Sites” zusammengefasst. Inzwischen gibt es in Buchform  bereits den Nachfolger “Even faster Web Sites” mit noch mehr Optimierungs-Tips.

Sein Fazit: Schuld an der Zähigkeit vieler Seiten ist selten das Backend mit dem die Inhalte generiert werden, sondern das Frontend, also die Auslieferung und die Art, wie Seiten aufgebaut sind und im Browser gerendert werden. Nach seiner Erfahrung entfallen durchschnittlich nur 20 Prozent der Gesamtzeit auf das Backend, aber 80 Prozent auf das Frontend.

Optimierungen des Backends sind typischerweise mit viel Aufwand verbunden, erreichen dann aber doch meist nur Beschleunigungen im 1-stelligen Prozentbereich. Demgegenüber kann man mit vernachlässigbarem Einsatz die 80 Prozent in großen Teilen reduzieren. Selbstverständlich gilt das nur, wenn man nicht das langsamste Recherche-Backend aller Welten hat, wie es bei Recherchen in klassischen SQL-basierten Katalogen bei Begriffen wie “Deutschland” oder “Geschichte” auch heute manchmal immer noch der Fall ist – mal abgesehen, wie sinnlos so eine Recherche eigentlich ist…

Aufbauend auf dem angesprochenen Buch gibt es ein Firefox-Add-on von Yahoo! mit dem sinnigen Namen YSlow – “Why Slow?”, das in das beliebte Firebug-Add-on integriert ist.

Nach der Installation des YSlow Add-ons kann man es auf eine beliebige Webseite, sei sie aus einer statischen Web-Präsenz oder einem Bibliothekskatalog anwenden. Entsprechend der Optimierungsbereiche aus dem erstgenannten Buch wird der jeweiligen Webseite zunächst eine erreichte Bewertungs-Punktzahl und eine allgemeine Note von A (sehr gut) bis F (sehr schlecht) gegeben. Darauf folgen die “Einzelwertungen” in den jeweiligen Bereichen. Wie im Buch sind die Bereiche von “bringt viel” bis “bringt auch noch etwas” von oben nach unten geordnet – daraus gibt sich dann automatisch die Reihenfolge, mit der man potentielle Defizite angehen kann.

Sehr gelungen ist hier, dass neben den Bewertungen unter “Read more” direkt per Mausklick weitergehende Informationen auf den Webseiten von Yahoo!s Developer Network mit einer Kurzfassung der Tips aus dem Buch angeboten werden.

Nach Lektüre des Buches habe ich dann auch sofort YSlow installiert und auf den KUG und weitere Kataloge losgelassen. Das Ergebnis war dann jedoch etwas enttäuschend. Gerade mal ein D-E hatte der KUG erreicht. Angemäkelt wurde insbesondere folgendes

  • Weniger HTTP-Request machen
  • Ein Content Delivery Network verwenden
  • Für viele statische Inhalte langlebige Expires-Header hinzufügen
  • Auslieferung mit gzip komprimieren
  • ETags konfigurieren

Bei vielen anderen Bibliothekskatalogen, die ich mir mit YSlow angeschaut habe, sind es oft die gleichen Optimierungsbereiche. Die Behebung der gravierendsten Mängel war danach sehr einfach, auch wenn wir uns z.B. nicht in ein CDN wie Akamai mit dem KUG einkaufen werden…

Zuerst haben wir CSS- und JavaScript Files reduziert und so gut es ging zusammenfasst. Die Verwendung von CSS-Sprites für die Icons steht noch aus, da hier auch noch der Bereich Barrierefreiheit mit bedacht werden muss.

Für den KUG verwenden wir ein Setup mit gestaffelten Web-Servern. Das eigentliche Applikations-Backend bildet ein Apache2. Vor diesen haben wir einen weiteren Web-Server postiert, der alle statischen Inhalte ausliefert, also Stylesheets, JavaScript, Bilder usw. Alle Anfragen an die KUG-Anwendung werden per ProxyPass an den Apache2 weitergereicht. Auf diese Weise muss sich der “dicke” Apache2 mit mod_perl

  1. nicht mit den statischen Anfragen abplagen und kann
  2. seine Inhalte schnell an den vorgeschalteten Webserver ausliefern. Damit wird er nicht von Anfragen über langsame Datenverbindungen blockiert.

Als vorgeschaltete Web-Server setzen wir hier teils noch den Apache 1.3, teils lighttpd ein. Dementsprechend müssen dort die weiteren Optimierungen erfolgen.

Expires

Da wären dann die Expires-Header für die statischen Inhalte. Diese sollten auf mehr als 30 Tage gesetzt werden.

Bei Apache 1.3 geht das mit mod_expire und den Direktiven

<IfModule mod_expires.c>
<LocationMatch “^/(js|styles|images)/”>
ExpiresActive on
ExpiresDefault “access plus 30 days”
</LocationMatch>
</IfModule>

Bei lighttpd mit:

server.modules   += ( “mod_expire” )

expire.url = ( “/images/” => “access plus 30 days”, “/styles/” => “access plus 30 days”, “/js/” => “access plus 40 days” )

Komprimierte Auslieferung

Ein weiterer Bereich ist die komprimierte Auslieferung der Daten mit gzip.

Bei Apache 1.3 geht das sehr einfach mit mod_gzip:

<IfModule mod_gzip.c>
mod_gzip_on                   Yes
mod_gzip_can_negotiate        Yes
mod_gzip_static_suffix        .gz
AddEncoding              gzip .gz
mod_gzip_update_static        No
mod_gzip_command_version      ‘/mod_gzip_status’
mod_gzip_temp_dir             /tmp
mod_gzip_keep_workfiles       No
mod_gzip_minimum_file_size    500
mod_gzip_maximum_file_size    500000
mod_gzip_maximum_inmem_size   60000
mod_gzip_min_http             1000
mod_gzip_handle_methods        GET POST
mod_gzip_item_exclude         reqheader  “User-agent: Mozilla/4.0[678]”
mod_gzip_item_include         file       \.html$
mod_gzip_item_include         file       \.js$
mod_gzip_item_include         file       \.css$
mod_gzip_item_include         file       \.pl$
mod_gzip_item_include         handler    ^cgi-script$
mod_gzip_item_include         mime       ^text/html$
mod_gzip_item_include         mime       ^text/plain$
mod_gzip_item_include         mime       ^text/css$
mod_gzip_item_include         mime       ^application/javascript$
mod_gzip_item_include         mime       ^application/x-javascript$
mod_gzip_item_include         uri       ^/styles/
mod_gzip_item_include         uri       ^/js/
mod_gzip_item_exclude         mime       ^image/
mod_gzip_dechunk              Yes
LogFormat                     “%h %l %u %t \”%V %r\” %<s %b mod_gzip: %{mod_gzip_result}n In:%{mod_gzip_input_size}n -< Out:%{mod_gzip_output_size}n = %{mod_gzip_compression_ratio}n pct.” common_with_mod_gzip_info2
CustomLog                     /var/log/apache/mod_gzip.log common_with_mod_gzip_info2
mod_gzip_add_header_count     Yes
mod_gzip_send_vary            On
</IfModule>

Entsprechendes geht bei lighttpd mit mod_compress und

compress.cache-dir          = “/var/cache/lighttpd/compress/”
compress.filetype           = (“text/plain”, “text/html”, “application/x-javascript”, “text/css”, “application/javascript”)

Diese Direktiven stehen so bereits in der Konfigurationsdatei lighttpd.conf und müssen dort nur aktiviert werden.

ETags

Weiter geht es mit der Konfiguration von ETags. Hier bietet es sich an, diese komplett zu deaktivieren

In Apache 1.3 geht das mit:

<LocationMatch “^/(js|styles|images)/”>
FileEtag None
</LocationMatch>

Entsprechend in lighttpd:

static-file.etags = “disable”

Mit diesen wenigen Maßnahmen konnten wir uns von D-E im KUG auf eine Bewertung von B steigern. Auf unserem Entwicklungssystem für die nächste KUG-Version konnten wir die HTTP-Request weiter minimieren und haben es sogar auf ein A geschafft.Und das alles durch minimale Konfigurationsänderungen, die nicht wirklich Arbeit gekostet haben.

Selbstverständlich gibt es auch Bereiche, wo man mehr oder weniger machtlos ist, weil man z.B. externe Cover einbindet und der Lieferant keine Expires setzt oder nicht komprimiert oder … Manchmal kann man aber auch selbst dann noch optimieren. So ist bei uns auf allen Rechercheseiten der BIX-Zählpixel eingebunden, der natürlich prompt angemäkelt wurde. Da dieser aber ohnehin nur für einen definierten Zeitraum von wenigen Wochen pro Jahr aktiv ausgewertet wird, haben wir dessen Integration in die KUG-Seiten einfach parametrisiert. So wird er jetzt nur noch im Auswertungszeitraum aktiviert und erscheint sonst auf unseren Seiten nicht mehr.

Insgesamt hat das YSlow Add-on bei der Optimierung des KUG als Beispiel für einen Bibliothekskatalog gute Dienste geleistet. Gerade durch die Adressierung konkreter Optimierungsbereiche und seine Evaluationmöglichkeiten deckt es den Frontendbereich sehr gut ab. Weitere Blog-Artikel zu YSlow finden sich hier und hier.