Datenstrom komprimieren

Wir versuchen oft an den Performance-Schrauben im PHP-Script zu drehen, trotzdem wird das Laden der Seite bei den Besuchern dadurch irgendwann nicht mehr beschleunigt, wenn man die gröbsten Performance-Schnitzer ausgemerzt hat. Man kommt schnell zu dem Schluss, dass da nix mehr zu machen sei, weil der Client einfach eine zu langsame Internetverbindung hat.

Gut, mittlerweile haben viele Leute DSL und schnellere Internetverbindungen und trotzdem surfen noch eine Menge anderer Leute mit einem 56k Modem. Selbst schuld? Diesem Motto sollte man sich nicht anschließen, denn in keinem anderen Medium ist der Schritt zu der Konkurrenz ein so kleiner wie im Internet. Der User merkt, dann die Seite ihm zu lange lädt, er klickt auf Abbrechen (wenn er gutmütig ist vielleicht auch erst auf Aktualisieren, merkt dann aber, dass die Seite wirklich so lange zum Laden braucht) und tippt die URL der Konkurrenz in den Browser – weg ist er. Aber ist es wirklich so, dass wir die Ladezeit beim Client nicht Beschleunigen können?

Gegenfrage: Gäbe es diesen Beitrag wenn dem so wäre 😉
Haben Sie schon einmal auf die Verbindungsdetails einer Netzwerkverbindung geschaut (egal, ob LAN, DSL oder Modem)? Da steht etwas von Komprimierung. Ha, da haben wirs !!! Wir müssen einfach weniger Daten aber mit dem gleichen Inhalt senden.

In PHP (ab Version 4 und mit installierter zlib) funktioniert das recht einfach: Wir setzen einen Ausgabepuffer ein. Dadurch wird die letztliche Ausgabe, die unser Script erzeugt, nachdem das Scrpt fertig durchgearbeitet wurde, per gzip auf etwa ein Drittel der Originalgröße gepackt und diese gepackten Daten anschließend an den Client geschickt. Seit Jahren unterstützen moderne Browser (ja, ich zähle den IE hier mal eiskalt dazu 😉 ) das Entpacken dieses Datenstroms und können diesen dann korrekt darstellen.

In PHP:

//ganz am Anfang des Scripts aufrufen
ob_start("ob_gzhandler");
 
echo "Mein ganzer HTML-Code";

Wenn ob_gzhandler als Callback-Funktion von ob_start() verwendet wird, kann es auch überprüfen, ob der anfragende Browser dieses Content-Encoding überhaupt untersützt. Denn wir wollen ja Uralt-Browser nicht ausschließen.

Einige Nachteile hat die Komprimierung aber schon:

  • Man kann den Kompressionsgrad nicht einstellen und ist von den PHP Einstellungen abhängig
  • Man kann die Kompression nicht für einzelne Dateitypen ausschalten
  • Es gibt in PHP immer wieder Probleme, wenn man den Puffer leeren muss

Hx3 war so freundlich genau für diese Nachteile eine PHP-Klasse zu schreiben, die diese Nachteile behebt und trotzdem alle Vorteile beibehält. Hinzu kommt, dass die Klasse je nach Serverauslastung stärker oder weniger stark komprimiert (dazu ist allerdings ein eigener Server nötig, auf Shared Hosting glaube ich nicht, dass man auf die Datei ‚/proc/loadavg‘ zugreifen kann. Sollte man sich einmal ansehen – es wird nichts revolutionäres gemacht, aber wenn man der PHP-Funktion ob_gzhandler nicht so ganz traut, weil man zu wenig einstellen kann, dem sei dieses Tutorial ans Herz gelegt.

Jan hat 152 BeitrÀge geschrieben

10 Kommentare zu “Datenstrom komprimieren

  1. Tim sagt:

    Danke für den Tipp. Die Klasse ist nützlich. Leider habe ich einen Windows Server, sodass nicht alle Funktionen (z. B. Berechnung der Serverauslastung) auch unter Windows lauffähig sind. Gibt es für Windows eine Funktion, um die aktuelle Serverlast auszulesen? Am besten wäre eine Möglichkeit ohne externen Programmen, da der Aufruf z. B. mit shell_exec den Ladevorgang des Scripts deutlich verlangsamt. Bei jedem Zugriff wird eine neue Instanz des Programmes erstellt! Eine Möglichkeit wäre das Ergebnis zu cachen, sodass nur jede 1 Sekunde ein Aufruf stattfinden kann. Eine andere (und wahrscheinlich die beste), wäre eine Extension für PHP zu schreiben, welche die Serverauslast berechnet. Das ist mit Delphi dank PHP4Delphi möglich. Allerdings hatte ich noch keine Zeit mich damit zu beschäftigen.

    Gruß
    Tim

  2. Marco sagt:

    Ich habe noch drei Fragen zu diesem Thema:

    1) Muss man am Ende des Scripts noch das „ob_end_flush();“ schreiben?
    2) Muss man nicht noch prüfen, ob der Client überhaupt GZip unterstützt?
    3) Ist der folgende Codeschnipsel aus einem anderen Artikel hier nicht besser?
    ######
    if (stripos($_SERVER[„HTTP_ACCEPT_ENCODING“],’x-gzip‘) !== false) {
    header(„Content-encoding:x-gzip“);
    $buffer = gzencode($buffer);
    }
    elseif (stripos($_SERVER[„HTTP_ACCEPT_ENCODING“],’gzip‘) !== false) {
    header(„Content-encoding:gzip“);
    $buffer = gzencode($buffer);
    }
    elseif (stripos($_SERVER[„HTTP_ACCEPT_ENCODING“],’deflate‘) !== false) {
    header(„Content-encoding:deflate“);
    $buffer = gzdeflate($buffer);
    }
    ######

  3. Jan sagt:

    zu 2. + 3.: Das Überprüfen des Client-Request-Header-Feldes Accept-Encoding übernimmt die Funktion ob_gzhandler().

    zu 1.: ob_end_flush() ist nur notwendig, wenn per php.ini dies so eingestellt wurde. Ansonsten wird der Puffer am Ende des Scripts automatisch geleert.

  4. Marco sagt:

    Aber zur Sicherheit kann man immer am Ende „ob_end_flush()“ schreiben, oder? Es wird also auf keinen Fall schaden!?

    Wenn die Überprüfung von „Accept-Encoding“ von der Funktion übernommen wird, kann man doch jede Seite per GZip verkleinern, oder? Wenn der Client das nicht unterstützt, wird die Seite halt nicht komprimiert, richtig?

  5. Jan sagt:

    Schaden tut ein ob_end_flush nicht, denke ich.

    Korrekt, Du kannst alle Dokumente per ob_gzhandler behandeln. Wenns der browser nicht untersützt oder das entsprechende Headerfeld nicht sendet, bleibts unkomprimiert. Das ist ja das tolle an der Funktion 😉
    Und die minimal höhere Prozessorauslastung für die Komprimierung fällt wirklich nicht ins Gewicht im vergleich zu den Vorteilen.

    Allerdings immer mal kurz abwägen, ob es Sinn macht. Wenn Du ein 300 Byte-Stylesheet per gzip komprimierst, hats vielleicht nur 100 Byte, aber dann würde ichs lassen, denn der prozessoraufwand für die paar Byte ist größer als der Nutzen.

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>