Facebook
Twitter
Google+
Kommentare
3
Willkommen bei "the web hates me". Mittlerweile hat unser Team ein tolles neues Monitoringtool für Agenturen gelauncht. Unter dem Namen koality.io haben wir einen Service geschaffen, der er Agenturen ermöglicht mit sehr geringen Kosten alle Ihre Webseiten zu überwachen.

Virtuelle Javascript-Datei

Wie der Blog-Eintrag „Performancegewinn durch virtuelles Javascript-File“ ganz richtig bemerkt kann es sinnvoll sein eigentlich statische Dateien nicht vom Web-Server direkt ausliefern zu lassen sondern den Umweg über PHP zu machen.

Um es nochmals zusammenzufassen hängt die „gefühlte“ Ladezeit – also die Zeit die der Browser nach dem Eintippen der URL zum eigentlichen Laden der Inhalte, darstellen der selben bis er wieder auf Eingabe des Benutzers wartet – von verschiedenen Faktoren ab:

  • Datenmenge
  • Komplexität der Inhalte
  • Rechenleistung
  • Datenübertragungsrate
  • Antwortzeit
  • Übertragungsprotokoll

Als erstes würde sicherlich jedem zu diesem Thema die Datenmenge einfallen. Schließlich gilt allgemein, dass sich 1 KByte Daten schneller über das Netzwerk übertragen lässt als 1 MByte. Gleiches gilt für die Verarbeitung: wenige Daten lassen sich schneller verarbeiten. Daher ist es grundsätzlich eine gute Idee Web-Seiten schlank zu halten und z.B. die verwendeten Grafiken auf Dateigröße hin zu optimieren.

Sind die Daten erstmal beim Browser angekommen, spielt die Komplexität der Inhalte eine wesentliche Rolle beim Aufbau der Seite. Generell sind DIVs einem Layout mit verschachtelten Tabellen vorzuziehen. Allerdings bremsen auch Flash-Filme, animierte Grafiken oder die Verwendung anderer Plugins die gefühlte Performance eine Web-Seite aus. Viele, große Bilder auf einer Web-Seite müssen vom Browser im Arbeitsspeicher gehalten werden. Wird dieser knapp kommt es zwangsläufig zu Verzögerungen.

Natürlich spielt auch die pure Rechenleistung der Hardware eine große Rolle. Heutzutage bringen die meisten Systeme mehr als genug davon mit. Selbst die kleinen Stromsparrechner takten den Prozessor mit 1,6GHz und moderne PCs sind sowieso mit Dual-Core 64Bit Prozessoren ausgerüstet. Es ist also gar nicht so leicht einen halbwegs modernem Computer mit einer Web-Seite ins Schwitzen zu bringen – Schweißperlen sind meist ein Zeichen für amoglaufende Javascript-Anweisungen oder Probleme mit Plugins.

Damit komme ich nun endlich zu den netzwerkspezifischen Performance-Killern. Zuallererst steht da die Übertragungsrate. Ich will jetzt gar nicht von abgelegenen australischen Farmen reden – auch in Deutschland sind die zur Verfügung stehenden Bandbreiten sehr unterschiedlich. Dabei ist der Ausbau in urbanen Gebieten zumeist sehr gut – 20MBit/s für den Privatanwender sind hier keine Seltenheit. Gleichzeitig muss man sich auf dem Lande – fern jeder Vermittlungsstelle – mit weniger als 384kBit/s begnügen. Es macht also durchaus Sinn das Brutto Datenvolumen einer Web-Seite möglichst gering zu halten. Bei der Übertragungsgeschwindigkeit wird gerne die Antwortzeit vergessen. Während die (Netzwerk-) Wege innerhalb Deutschland recht kurz sind, kann im internationalen Datenverkehr schon mal eine halbe Sekunde vergehen bis die angepingte Gegenstelle mit einem Pong antwortet. Werden also nacheinander viele Dateien vom Web-Server angefordert dauert es viele halbe Sekunden bis die Antworten ankommen – selbst wenn die Daten dann mit 100MBit/s durchs Netz sausen.

Schließlich spielt noch das verwendete Übertragungsprotokoll HTTP und wie es von den Browsern verwendet wird ein Rolle. Prinzipiell wird bei HTTP bei jeder Anfrage an den Web-Server eine neue (TCP) Verbindung geöffnet und hinterher wieder geschlossen. Jeder Verbingungsaufbau kostet Zeit. Da eine „normale“ Web-Seite aus mehreren Dateien besteht (eine index.html, eine CSS-Datei, ein paar Hintergrundgrafiken und eine Hand voll Bilder) müssen viele Anfragen gestellt werden. Es müssen also viele Verbindungen aufgebaut und wieder geschlossen werden. Aus diesem Grund erlauben es die meisten Web-Server die Verbindung eine gewisse Zeit offen zu halten damit sie zum Transport gleich mehrerer Dateien verwendet werden kann. Das spart Zeit. Gleichzeitig versuchen die Browser die zur Verfügung stehende Bandbreite besser auszunutzen indem sie mehrerer (Firefox: zwei) Verbindungen gleichzeitig öffnen und zwei Dateien parallel herunterladen. Im Umkehrschluss bedeutet das, der Browser lädt nur soviele Dateien gleichzeitig vom Web-Server herunter wie Verbindungen offen sind (Firefox: zwei). Wird also in der index.html auf fünf Javascript-, drei CSS-Dateien und zwölf Grafiken verwiesen, lädt Firefox zwei Dateien gleichzeitig herunter und 18 Dateien müssen warten bis sie dran sind.

Soweit also zur Welt in der wir leben. Doch was hat das alles mit Javascript-Dateien zu tun? Ganz einfach: sie müssen vom Web-Server ausgeliefert und vom Browser verarbeitet werden – all das braucht kostbare Zeit die es zu sparen gilt.

Caching

Ich verwende gerne prototype.js, andere mögen JQuery bevorzugen. Wie auch immer muss die protoype.js an den Browser ausgeliefert werden. Das sind über 120 kByte an Daten! Zum Glück liefert der Web-Server zu der Datei auch noch ein paar Informationen die zum Caching verwendet werden können mit aus. Z.B. wann die Datei zum letztem Mal geändert wurde oder sich bis dann-und-dann nicht ändern wird. Bei einer erneuten Anfrage der selben Datei kann der Web-Server also einfach antworten: „304 Not Modified“ und muss die 120 kByte nicht nochmal übers Netz schaukeln. Diese Art von Caching funktioniert prima bei statischen Inhalten und wird automatische vom Web-Server verwaltet.

Packen

Dennoch 120 kByte sind ein ganz schöner Klotz. Da es sich um eine reinen Text handelt lässt sich die Datei gut packen. Auch das können Browser und Web-Server. Mit GZip gepackt sind es plötzlich nur noch 30 kByte – also 25% der Orginalgröße, das hat sich gelohnt. Um Javascript-Dateien klein zu kriegen gibt es auch Werkzeuge die alle nicht relevanten Daten aus den Dateien entfernen. JSmin ist so ein Kandidat der alle Kommentare und Formatierungen entfernt. Übrig bleiben furchtbar unleserliche Dateien, die dafür kleiner sind. Wendet man JSmin auf prototype.js an reduziert sich die Datei auf 95 kByte das GZip auf 23 kByte schrumpfen lässt. Auch dieser Aufwand hat sich gelohnt.

Zum Vergleich dazu die Dateigrößen der builder.js (eine Datei von script.aculo.us) und ContentArea.js (eine gut dokumentierte Javascript Klasse aus dem Wombat-Framework):

prototype.js
orginal: 129738 Byte
jsmin: 95327 Byte
gzip: 30237 Byte
jsmin + gzip: 12946 Byte

builder.js
orginal: 4770 Byte
jsmin: 2999 Byte
gzip: 1870 Byte
jsmin + gzip: 1279 Byte

ContentArea.js
orginal: 1204 Byte
jsmin: 441 Byte
gzip: 561 Byte
jsmin + gzip: 287 Byte

Um von dieser Möglichkeit zu profitieren müssen lediglich auf dem Produktiv-Web-Server die originalen Javascript-Dateien mit den minimierten ersetzt werden. Der Web-Server kann die Dateien dann beim Ausliefern mit GZip (oder Deflate) packen.

Sammeln

Und damit komme ich endlich auf den Begriff der virtuellen Javascript-Dateien. Eingang erwähnter Blog-Eintrag schlägt vor viele Dateien auf einen Schlag auszuliefern. Da er es zum Entwickeln praktischer findet wenn Javascript-Code auf mehrere Dateien verteilt ist – ich übrigens auch – möchte er diese Ordnung im Dateisystem nicht stören. Um dennoch aus den vielen Javascript-Dateien eine zu machen werden sich nicht mehr direkt sondern indirekt über ein PHP-Skript, welches einfach alles Dateien der Reihe nach einliest ausgegeben. Diese Kind nennt er virtuelle Javascript-Datei.

Ein sehr treffender Name, wie ich finde. Die Technik ist aber nicht ganz neu und bei weitem ausgereifter als in seinem Beitrag beschrieben. Zum einen hat er die beschriebenen Möglichkeiten des Caching und zum Packen der Inhalte vernachlässigt. Zum anderen besteht seine virtuelle Javascript-Datei aus einer Reihe von include-Anweisungen (noch schlimmer: require_once) die, die echten Javascript-Dateien nacheinander einlesen. Das hat zum einen einer „statischen“ Datei sehr nahe und zum anderen muss dieses Skript immer nachgepflegt werden wenn sich etwas in den Abhängigkeiten zu den Javascript-Dateien ändert.

Wesentlich objektorientierter, einfacher zu handhaben und flexibler sind die Javascript-Tools aus dem Wombat-Framework. Wie in der Dokumentation beschrieben werden die virtuellen Javascript-Dateien dynamisch zusammengestellt und beinhalten daher immer nur die Dateien die auch wirklich benötigt werden. Weitere Tools um Javascript-Code „onload“ auszuführen oder Javascript-Klassen via AJAX nachzuladen runden das Featureset ab.

Es geht noch besser

So komfortabel virtuelle Javascript Dateien auch sein mögen und so groß der Leistungszuwachs sein mag, eigentlich ist es überflüssig statische Dateien mit PHP auszuliefern. Bei genauerer Betrachtung ist leicht festzustellen, dass PHP nur zum Sammeln und Minimieren der Javascript-Dateien gebraucht wird. Ausliefern kann der Web-Server die Dateien auch alleine.

Im Prinzip muss doch das PHP-Skript nur einmal laufen um die virtuelle Javascript-Datei zu erstellen. Da sich die Ausgabe der Datei bis zum nächsten Softwareupdate nicht mehr ändert kann der Web-Server dann die Dateien direkt ausliefern.

Wer einen Apache-Web-Server hat und über vollen Zugriff auf dessen Konfiguration verfügt kann für jedes Verzeichnis spezielle Fehler-Seiten einstellen. Diese Seiten werden dann z.B. ausgeliefert wenn eine Datei nicht existiert. Existiert eine „virtuelle“ Javascript-Datei nicht, muss nur das PHP-Skript aufgerufen werden, dass eben diese erzeugt. Das Skript legt die Datei an und liefert sie aus. Bei der nächsten Anfrage nach dieser virtuellen Javascript-Datei existiert die Datei bereits und der Fehler tritt nicht mehr auf. Auf diese Weise wird PHP immer nur dann bemüht wenn wirklich eine neue virtuelle Javascript-Datei erzeugt werden muss, ansonsten kann sie der Web-Server direkt ausliefern. Sollen die virtuellen Javascript-Dateien neu generiert werden, müssen sie einfach gelöscht werden.

Eine weitere Möglichkeit bietet das Apache-Module MemCache. Damit lässt sich der Web-Server so einstellen, dass er bestimmte Inhalte im Speicher hält. Der Apache-Server würde als beim ersten Mal das PHP-Skript bemühen um die virtuelle Javascript-Datei zu erzeugen und alle weiteren Anfragen direkt aus dem Speicher beantworten. Dazu braucht der Web-Server natürlich ein bisschen RAM, auf der anderen Seite muss er aber noch nicht mal mehr auf das Dateisystem zugreifen um das Javascript auszuliefern. Im übrigen lässt sich das Apache-Module MemCache auch für „normale“ statische Inhalte wie Bilder oder CSS-Dateien verwenden. Soll der Cache gelöscht werden muss einfach der Apache neu gestartet werden.

Apropos CSS. Die beschriebenen Methoden virtuelle Javascript-Dateien zu erzeugen lassen sich auch für virtuelle CSS-Dateien anweden. Natürlich wird JSmin nicht funktionieren, aber ich bin mit sicher, dass sich in den Weiten des WWW bestimmt das eine oder andere Werkzeug finden lässt mit dem sich CSS-Dateien entschlacken lassen.

Über den Autor

gERD Schaufelberger

Kommentare

3 Comments

  1. Sinnvoller ist es allerdings, solche Dateien (wenn es denn wirklich nötig ist, sie aufzusplitten) im Deployment-Prozess zusammenzuführen, einzudampfen und – genau wie Grafiken – von einem Webserver ausliefern zu lassen, der auf statische Inhalte spezialisiert ist. lighttpd wäre da zum Beispiel eine exzellente Wahl (nicht out of the box, aber schnell eingerichtet).

    So oder so. Einen tatsächlichen Gewinn wird man ohnehin erst haben, wenn

    Reply
  2. Ich würds genauso wie unset machen.
    Interessant ist sicher auch der YUI Compressor.

    http://developer.yahoo.com/yui/compressor/

    Ach und sollte man die Dateien vom webserver bloß ausliefern lassen. Muss man immer „Content-Encoding: gzip“ als Header mitsenden, aber das sollte eigentlich klar sein. Sonst kommt ja nur Buchstabensalat an..

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen