Sicherheitswarnung: Bitte keine Version älter als 1.11 verwenden!

Einsparungen durch die Auslieferung komprimierter Daten

Einleitung

Mit diesen und ähnlichen Fragen soll sich das vorliegende Dokument beschäftigen.

Ein Beispiel einer Traffic-Analyse

Zur Veranschaulichung der Materie möchte ich ein praktisches Beispiel anhand meiner eigenen Domain liefern.

Die nachfolgenden Berechnungen sind in dieser Ausführlichkeit nicht für jeden Anwender erforderlich - ich wollte nur einfach mal etwas genauer wissen, was in meiner Domain eigentlich so alles passiert und wie gut gzip_cnc dafür überhaupt verwendbar ist.

Daher habe ich den Traffic einer hinreichend großen Zeitspanne (noch ohne den Einsatz eines Komprimierungswerkzeugs), basierend auf den Ergebnissen einer Webalizer-Auswertung und ein bißchen Nachverarbeitung, analysiert (mit Hilfe einer Standard-Spreadsheet-Software) und bin dabei zu diesen Ergebnissen gekommen:

Dokumenttyp Hits kBytes Traffic Größe Verpackung
Statische HTML-Seiten 985316,30%7294455,03%8279742,90%860511.90%
Bilder (GIF) 3156852,24%1811313,66%4968125,74%161263.54%
Dynamische HTML-Seiten (SSI) 637310,55%2316117,47%2953415,30%474521.58%
Cascading Style Sheets 686011,35%53864,06%122466,35%182856.02%
Bilder (JPG) 36205,99%39272,96%75473,91%213547.97%
ZIP-Archive (Downloads) 260,04%55874,21%56132,91%2210660.46%
CGI-Programme 6681,11%21911,65%28591,48%438323.36%
JavaScript-Code 12902,13%11230,85%24131,25%191553.46%
Bilder (FavIcon) 570,09%1070,08%1640,08%294634.76%
Text-Dateien (robots.txt) 1090,18%00,00%1090,06%1024100.00%
Bilder (PNG) 70,01%250,02%320,02%468121.88%

Auf den ersten Blick mag es abenteuerlich anmuten, 'Hits' und 'kBytes' zu 'Traffic' zu addieren.

Tatsächlich aber trifft dies die Realität ziemlich genau. Unabhängig von den tatsächlich übertragenen Nutzdaten besteht jeder HTTP-Request aus einem HTTP-Header des Browsers und einem HTTP-Header des Servers in dessen Antwort - beide liegen typischerweise im Bereich zwischen 250 und 450 Bytes. Dazu noch ein paar Bytes Verpackung auf niedrigeren Protokoll-Ebenen (TCP/IP-Pakete enthalten ja auch nicht nur Nutzdaten), und der grundsätzliche Overhead einer jeden HTTP-Anforderung kommt sehr schnell in den Bereich von 800-1000 Bytes.

Merke also: Auf HTTP-Ebene gibt es gar keine 'kleinen' GIF-Bilder von ca. 50 Bytes, weil für diese das Zwanzigfache ihrer eigenen Größe als Verpackung für Anforderung und Antwort benötigt werden.

Erzielbare Einsparungen

Mit gzip_cnc lassen sich nur Inhalte statischer HTML-Seiten komprimieren. Das betrifft bei meiner Domain 55% der Nutzdaten und 43% des übertragenen Volumens. Es bedeutet allerdings auch, daß unser Handler nur bei jeder sechsten Anforderung aktiviert werden muß.

Die genannten Anteile des Datenvolumens schrumpfen durch die Verwendung komprimierter Auslieferung allerdings keineswegs auf Null. Die erzielbare Einsparung pro Dokument ist von zahlreichen Faktoren abhängig; die vorliegenden Seiten der Dokumentation zu gzip_cnc selbst können jedoch als Richtgröße dienen. Diese Seiten sind zwischen 10 und 20 kB groß (also doppelt so groß wie der Durchschnitt meiner Domain laut obiger Tabelle); ihr Inhalt wird um 65-72% reduziert, also um gut zwei Drittel. Das deutlich größte Dokument, der gzip_cnc-Quelltext, hat mit 82% auch die beste Komprimierungsrate.

Im Allgemeinen ist ein Erwartungswert von 70% für 'normale' HTML-Dokumente vernünftig - bei schlechter Code-Qualität (wie sie von vielen Code-Generatoren erzeugt wird) und hohem Tabellenanteil können es auch leicht 80% oder mehr werden. (Ich habe in meiner Domain eine Datei, welche durch Komprimierung um nicht weniger als 96%, also Faktor 25, reduziert wird ...)

Für meine eigene Domain würden 70% Einsparung pro HTML-Dokument eine Einsparung von knapp 40% aller Dokument-Nutzinhalte (welche ggf. von den Providern als Maß für traffic-begrenzten Webspace herangezogen werden) und knapp 30% des tatsächlichen Übertragungsvolumens (und damit über 40% kürzere Antwortzeiten für meine Besucher) bedeuten. Dies halte ich für erreichbare Werte bei einem Großteil der üblichen kleinen bis mittleren Homepages.

Leistungsvergleich mit mod_gzip

Das Apache-Modul mod_gzip könnte insbesondere auch die Ausgaben dynamischer HTML-Dokumente (CGI-Anwendungen und Server Side Includes) verarbeiten und damit (unter Berücksichtung nur der HTML-Dokumente) 74% (Nutzdaten) bzw. 60% (Übertragungsvolumen) aller Anforderungen komprimieren.

Bei wiederum 70% Komprimierungsrate für HTML-Dokumente wäre nun eine Einsparung von 53% der Dokument-Nutzinhalte bzw. 42% des tatsächlichen Übertragungsvolumens (und damit über 70% kürzere Antwortzeiten für die Besucher) möglich.

Andere Dokumentarten als HTML

Die Zahlen des vorliegenden Beispiels zeigen, daß die Beschränkung von gzip_cnc auf HTML-Dokumente dessen Leistungsfähigkeit nur marginal einschränkt.

Da der (zur Zeit noch verwendete) Netscape-4.x-Browser weder JavaScript-Code noch Cascading Style Sheets aus separaten Dateien in komprimierter Form korrekt verarbeiten kann (diese jedoch per HTTP-Header in komprimierter Form anfordert), wäre eine Verarbeitung auch dieser Dokumentarten durch gzip_cnc inhaltlich betrachtet nicht ganz unkritisch. Würde man solche Dateien jedoch tatsächlich komprimiert auszuliefern versuchen, dann würde der Anteil der komprimierbaren Anforderungen in meiner Domain lediglich von 55% auf 60% (Nutzdaten) bzw. 43% auf 51% (Übertragungsvolumen) ansteigen.

Umgang mit dynamischen HTML-Seiten

Die in der obigen Beispiel-Rechnung aufgeführten Zahlen zeigen, daß innerhalb meiner Domain SSI relativ intensiv eingesetzt wird (immerhin ein Viertel aller HTML-Inhalte), welches nicht vom Einsatz von gzip_cnc profitieren kann.

Ursprünglich waren auch die vorliegenden Seiten der gzip_cnc-Dokumentation SSI-Dokumente (mit einer serverseitig bedingt berechneten und dynamisch eingefügten Navigationsleiste), aber um diese Seiten (deren Inhalt sich ja nicht dynamisch ändert) komprimiert ausliefern zu lassen, habe ich mir ein kleines Perl-Programm geschrieben, welches diese SSI-Dokumente (unter Verwendung des CPAN-Moduls LWP::Simple) via HTTP anfordert und die Ergebnisse in Form statischer Dateien auf meinem lokalen Rechner abspeichert - diese statischen Dateien können anschließend in meinen Webspace hochgeladen und dann von gzip_cnc komprimiert ausgeliefert werden.

Dies sei nur als Beispiel dafür erwähnt, daß es in manchen Fällen sogar sinnvoll sein kann, die Art der existierenden Dokumente den technischen Randbedingungen anzupassen, um das Übertragungsvolumen zu reduzieren und eine bessere Antwortzeit für seine Besucher zu ermöglichen. Ich selbst jedenfalls werde mir künftig den Einsatz von SSI für statische Inhalte gründlich überlegen.

Umgang mit JavaScript und Cascading Style Sheets

Ursprünglich waren die CSS-Definitionen der vorliegenden Seiten der gzip_cnc-Dokumentation in einer gemeinsamen externen CSS-Datei enthalten und wurden mit Hilfe des HTML-tags <link src="..."> referenziert (d. h. vom Browser durch eine zusätzlichen HTTP-Anforderung nachgeladen). Dies ermöglichte es einem Browser, diese CSS-Definitionen separat in seinem Cache aufzubewahren; es bedeutete jedoch, daß diese CSS-Definitionen nicht in komprimierter Form ausgeliefert werden durften (aus Rücksichtnahme auf den armen alten fehlerbehafteten Netscape 4).

Inzwischen sind die CSS-Definitionen in jedem dieser Dokumente direkt enthalten. Die Einbindung erfolgt - genau wie bei der Navigationsleiste - via Server Side Includes. Zudem sind die CSS-Definitionen nun auf mehrere Dateien verteilt, so daß jedes Dokument nur genau diejenigen Definitionen einbinden muß, die es auch tatsächlich selbst verwendet. Als Bestandteil eines HTML-Dokuments können nun auch die CSS-Definitionen komprimiert ausgeliefert werden; dabei schrumpft ihr Volumen auf ein Drittel der ursprünglichen Größe und ist damit bereits kleiner als ein Paar von HTTP-Headern für Anforderung und Antwort.

Für JavaScript würde sinngemäß dasselbe gelten.

Der Umgang der Browser mit Dateien in deren Cache

Nicht nur bei kleinen Dokumenten (etwa JavaScript oder Cascading Style Sheets), sondern auch bei immer wieder angeforderten Dateien (JPGs werden oft als gemeinsame Hintergrundbilder für eine große Zahl von Seiten eingesetzt) kann der mittlere Anteil der 'Verpackung', also des HTTP- und TCP-Overheads der Anforderungen, leicht 50% und mehr erreichen.

Verantwortlich dafür sind insbesondere unzählige Browserzugriffe zur Überprüfung der Gültigkeit des Cache-Inhalts. In einem großen Teil aller Anforderungen - bis zu 30-40%! - lautet der zwischen Browser und Server durchgeführte Dialog nämlich:

Je nach eingestellter Strategie überprüfen die derzeit verbreiteten Browser die Gültigkeit des Inhalts ihres Cache

Die letztgenannte Einstellung wäre zweifellos die sinnvollste aus der Sicht des Seitenanbieters - falls der Browser begreift, was er tun sollte.

Um dem Browser aber überhaupt einen Anhaltspunkt zu liefern, ob er den Server fragen soll oder nicht, muß der Server vorher (also bei der Auslieferung derjenigen Daten, die im Browser-Cache gespeichert sind) eine Information mitsenden, wie lange diese Daten als gültig angesehen sein sollen. Falls der Browser diese Information verstehen kann und will, sendet er während des entsprechenden Zeitraums tatsächlich keine weiteren (überflüssigen) Anforderungen an den Server.

Und die effektivste Komprimierung einer Anforderung ist nun mal deren vollständige Vermeidung. Deshalb übernimmt gzip_cnc an dieser Stelle die Funktion einer entsprechenden Server-Konfiguration und sendet entsprechende HTTP-Header-Informationen an den Browser, welche diesem die Aufbewahrung der ausgelieferten Seite für einen (konfigurierbaren) Zeitraum (voreingestellt sind 24 Stunden) nahelegt.

In der aktuellen Version beherrscht gzip_cnc die Behandlung des conditional GET (welche normalerweise vom Apache-Webserver erledigt wird) noch nicht selbst: Derzeit wird das vom Browser übermittelte Datum ignoriert und immer der Inhalt des angeforderten Dokuments als Antwort ausgeliefert. Solche Anforderungen werden allerdings nur relativ selten für HTML-Dokumente gestellt - sehr viel häufiger dagegen für Dateien, die in vielen anderen Dokumenten immer wieder referenziert werden: Bilder, CSS und JavaScript - welche allesamt von gzip_cnc bisher ohnehin nicht verarbeitet werden können.

Vielleicht wird das in einer kommenden Version des Programms trotzdem realisiert werden ... es ist lediglich ein bißchen lästige Rechnerei (die Datumsangabe im HTTP-Header If-Modified-Since ist nicht besonders auswertungsfreundlich formatiert).

(Michael Schröpl, 2002-09-08)