PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Traffic pro eingeloggtem User herausfinden

with 7 comments

Den Traffic einer ganzen Seite herauszufinden ist nicht sonderlich schwer in Zeiten von awstats, webalizer und diversen anderen Apache-Log-Analyzern.

Doch ich wollte damals bei meinem eigenen Browsergame den Traffic pro eingeloggtem User messen. Idee war damals, den „Free Accounts“ 100MB pro Monat zu schenken, falls mehr benötigt wird, muss ein Premium-Account her.

Doch wie stellt man das an? Das Apache-Log hilft nicht wirklich, denn dort kann man nicht die einzelnen (eingeloggten) User unterscheiden. Ich habe damals 3 Lösungen gefunden:

  • mit dem Output Buffer von PHP arbeiten, Stringlänge bestimmen und mitloggen, dann Seite an den Browser schicken
  • Irgendwie die IP-Adressen des Users merken, und dann das Apache-Log durchparsen und rechnen.
  • die Apache-Extension mod_log_sql

Möglichkeit 1 sieht dann ungefähr so aus:

<?php
ob_start();

// some html content here

$trafficbytes = strlen(ob_get_flush());
// insert traffic into database

?>

Habe es nie ausführlich getestet, aber ich denke es ist langsam (da das HTML erst am Ende geflushed wird anstatt zwischendurch schon Häppchen zu verschicken). Außerdem muß man sich noch um Bilder kümmern, denn die sollen ja auch mitgezählt werden. Dazu würde ich eine Rewrite-Rule empfehlen, die bei jedem Bildrequest noch schnell ein php-Script ausführt. Auch das beeinflusst natürlich die Performance des Besuchers und des Webservers.

Die zweite Möglichkeit ist nicht ganz einfach umzusetzen. Die IP-Adresse allein ist nicht sonderlich aussagekräftig, einen Benutzer damit zu verfolgen recht aufwändig. In Zeiten von Proxies (Grüße an AOL), Neueinwahl bei DSL, mehrere Benutzer hinter einem Heimrouter etc reicht das nicht aus, um darüber den Benutzer zu bestimmen, gerade wenn Bruder und Schwester gemeinsam über eine Leitung im Internet sind usw.

Die dritte Alternative ist das Apache-Modul, welches ich dann auch genutzt habe: mod_log_sql. Dieses bindet man einfach in die entsprechene Apache-Config ein, und schon wird ein zusätzliches Log erzeugt, nämlich in der Datenbank. Dies kann man entweder für alle Seiten tun, oder nur für bestimmte VirtualHosts. Welche Spalten dort gefüllt werden sollen, in welcher Reihenfolge und mit welchen Daten, konfiguriert man in der apache-Config. Hier eine Beispielkonfiguration (hier für Linux, für Windows sieht es ähnlich aus: .dll statt .so usw.):

LoadModule log_sql_module modules/mod_log_sql.so
LoadModule log_sql_mysql_module modules/mod_log_sql_mysql.so

LogSQLLoginInfo mysqli://apacheloguser:apachelogpwd@localhost/apachelogdb
LogSQLCreateTables On
LogSQLMachineID mylocalmaschine
LogSQLTransferLogFormat AbHIhMmpRSstTUuvz
LogSQLTransferLogTable ztraffic_access_log
#LogSQLNotesLogTable ztraffic_notes
LogSQLCookieLogTable ztraffic_cookies
#LogSQLHeadersInLogTable ztraffic_headers_in
#LogSQLHeadersOutLogTable ztraffic_headers_out
LogSQLWhichCookies UserID     # hier könnten noch mehr Cookies stehen

Man kann alternativ zur eigenen Cookie-Tabelle auch ein bestimmtes Cookie mit ‚c‘ und „LogSQLWhichCookie“ in die access_log schrieben lassen. Hier eine Liste der Informationen, die man speichern kann.

Damit das Modul funktioniert, muß man noch das Modul „unique“ aktivieren, entweder per

a2enmod unique_id

oder via Einkommentieren von

LoadModule unique_id_module modules/mod_unique_id.so

Apache reload. Wenn man danach eine Seite besucht, liest man folgendes im apache error log:

[Tue Sep 01 19:59:46 2009] [notice] mod_log_sql: child established database connection
[Tue Sep 01 19:59:46 2009] [error] mysql_query returned (1)
[Tue Sep 01 19:59:46 2009] [error] table does not exist, preserving query
[Tue Sep 01 19:59:46 2009] [error] table doesn't exist...creating now
[Tue Sep 01 19:59:47 2009] [error] tables successfully created - retrying query
[Tue Sep 01 19:59:47 2009] [notice] query successful after table creation

Die Tabelle wurde also erfolgreich angelegt. Das Ergebnis sieht dann so aus:

modlogsql0

Der Inhalt der Tabelle (nur 1 Zeile):

modlogsql

Wäre dieser Test-Request kein 304er (Not Modified), hätten wir bei „bytes_sent“ auch eine Traffic-Angabe.

Mit Hilfe der Cookie-Tabelle können wir dann zB einmal täglich den Traffic jeden Users berechnen und in die entsprechende User-Tabelle füllen. Die Log-Tabelle sollte man dann wieder aufräumen und nicht länger benötigte Zeilen löschen.

Übrigens ist das Modul nur für Linux supported. Unter Windows kann man es zwar auch kompilieren, das ist aber mehr hohem Aufwand verbunden. Ich habe damals (2004) durch Zufall eine kompilierte Version gefunden. Die ist aber heute nicht mehr brauchbar, da sie für eine sehr alte Apache-Version kompiliert wurde.

Wenn ihr Fragen habt, fragt! Oder wenn ihr evtl. bessere Lösungen kenn, her damit! Oder was könnte man noch alles anstellen mit diesen Apache-Logs in einer Datenbank?

—————————————————-

Weitere interessante Links zum Thema:

Anleitung für Debian Etch

Die offizielle Webseite des Moduls

Written by Michael Kliewe

September 1st, 2009 at 8:29 pm

Posted in PHP

Tagged with , , , , ,

Echo, Print und die Parameter

with 5 comments

Wenn man sehr viel mit Strings und Stringausgaben arbeitet, und dabei maximale Performance braucht, gibt es einige Tricks, die dabei helfen, die letzten Prozente rauszuholen.
Bedenkt also: Diese leichten Performance-Unterschiede bringen nur bei sehr großen Scripten etwas. Bei einer kleinen Webseite oder ähnlichem ist der Unterschied zu vernachlässigen, da zählt mehr die persönliche Vorliebe und Lesbarkeit.

Zuerst einmal: in PHP gibt es zur Ausgabe von Daten (das können ja auch Zahlen sein, nicht nur Strings) zwei einfache Funktionen: echo und print.

Diese beiden Funktionen sind etwas besonderes: Es sind Sprachkonstrukte, und keine Funktionen. Man kann sie also auch ohne Klammern benutzen, beispielsweise so:

echo 'Dies ist ein Text';
echo('Dies ist ein Text');

Aber das kann auch zu Problemen führen, beispielsweise in diesem Quelltext:

(1 === $i) ? echo 'gleich' : echo 'ungleich';

Bei dieser trinären Bedingung funktioniert echo nicht, da es keinen Rückgabewert hat. print funktioniert.

echo ist print immer vorzuziehen. Warum? echo ist leicht performanter (aber nur im tausendstel Bereich, siehe unten). Außerdem bietet es die Möglichkeit, mehrere Parameter zu übergeben. Auch das ist minimal schneller als einen String mit Punkten zu verbinden.

In den folgenden Scripten nutze ich jeweils den Output Buffer von PHP, um nur im Speicher zu arbeiten. Wenn man die ob_-Funktionen weglassen würde, würde ein sehr großer Teil der CPU-Belastung (und damit des Tests) auf die eigentliche Ausgabe in der Console (bzw. dem Browser) draufgehen und die Ergebnisse verfälschen.

<?php
$string1 = "_string1_";
$string2 = "_string2_";

$timeStartComma = microtime(true);
for ($i=0; $i<1000000; $i++) {
	ob_start();
	echo $string1, 'sometexthere', $string2, "\r\n";
	ob_get_clean();
}
$timeEndComma = microtime(true);

$timeStartDot = microtime(true);
for ($i=0; $i<1000000; $i++) {
	ob_start();
	echo $string1 . 'sometexthere' . $string2 . "\r\n";
	ob_get_clean();
}
$timeEndDot = microtime(true);

$timeStartPrint = microtime(true);
for ($i=0; $i<1000000; $i++) {
	ob_start();
	print $string1 . 'sometexthere' . $string2 . "\r\n";
	ob_get_clean();
}
$timeEndPrint = microtime(true);

echo "Comma needed " . ($timeEndComma-$timeStartComma) . " seconds\n";
echo "Dot needed " . ($timeEndDot-$timeStartDot) . " seconds\n";
echo "Print needed " . ($timeEndPrint-$timeStartPrint) . " seconds\n";
?>

echo1

<?php
$string1 = "_string1_";
$string2 = '_string2_';

$timeStartDouble = microtime(true);
for ($i=0; $i<1000000; $i++) {
	ob_start();
	echo "_string1__string1__string1__string1_";
	ob_get_clean();
}
$timeEndDouble = microtime(true);

$timeStartSingle = microtime(true);
for ($i=0; $i<1000000; $i++) {
	ob_start();
	echo '_string2__string2__string2__string2_';
	ob_get_clean();
}
$timeEndSingle = microtime(true);

echo "Double needed " . ($timeEndDouble-$timeStartDouble) . " seconds\n";
echo "Single needed " . ($timeEndSingle-$timeStartSingle) . " seconds\n";
?>

echo4

Es ist übrigens minimal schneller, wenn man keine Klammern nutzt.

<?php
$timeStartWithout = microtime(true);
for ($i=0; $i<1000000; $i++) {
	ob_start();
	echo '_string1_';
	ob_get_clean();
}
$timeEndWithout = microtime(true);

$timeStartWith = microtime(true);
for ($i=0; $i<1000000; $i++) {
	ob_start();
	echo('_string1_');
	ob_get_clean();
}
$timeEndWith = microtime(true);

echo "Without brackets needed " . ($timeEndWithout-$timeStartWithout) . " seconds\n";
echo "With brackets needed " . ($timeEndWith-$timeStartWith) . " seconds\n";
?>

echo5

Man beachte wie gesagt, dass wir hier von 1 Million Schleifendurchläufen reden. In 99,9% aller Fälle ist der Unterschied zu vernachlässigen. Wer also hoch performante Riesenscripte bauen möchte, sollte sich das ganze genau anschauen (oder solche Dinge gleich in C schreiben, aber ich will ja keine Werbung für andere Programmiersprachen machen 😉 )

Written by Michael Kliewe

September 1st, 2009 at 9:02 am

Posted in PHP

Tagged with , ,

Optimierung meines Blogs

with 2 comments

SEO ist in aller Munde. Es gibt Millionen von SEO-Experten, die einen „100%-ig auf Platz 1 bei Google“ bringen können. Natürlich für viel Geld, und mit komischen, teils verbotenen Methoden.

Ich verlasse mich da lieber auf die kleinen Tipps und Tricks, die man so liest, und die auch Sinn machen. Auch Google gibt Webmastern Tipps, um ihre Seiten gut indizierbar zu machen, und so einen guten Pagerank zu erhalten. Interessante Hintergrund-Informationen gibt es zB bei Google’s Webmaster Tools.

Ich möchte hier keine riesen Sammlung eröffnen, sondern nur das WordPress-Plugin „All in One SEO Pack“ vorstellen. Dieses Plugin hilft dabei, auf jeder Seite unterschiedliche und vor allem passende Keywords und Descriptions setzen zu lassen (automatisch extrahiert aus dem Artikel-Text). Auch den Titel kann man auf jeder Seite anpassen lassen. Des weiteren unterstützt es die Möglichkeit, doppelte Inhalte unter verschiedenen URLs zu vermeiden mit dem „canonical“-Tag.

Damit sind die wichtigsten Dinge erstmal abgedeckt. Alle anderen Meta-Tags sind heutzutage relativ unwichtig geworden, jedenfalls liest man das oft.

Schaut es euch selbst an, ich finde es sehr hilfreich, und werde es auch vorerst dabei belassen.

So sehen die Optionen aus:

seo

Written by Michael Kliewe

August 31st, 2009 at 7:46 pm

CSS aufräumen mit „Dust-Me Selectors“

with 8 comments

Ein tolles Firefox-Addon habe ich in meinem Repertoire gefunden, das ich euch vorstellen möchte: Dust-Me Selectors.

Damit kann man einfach seine Seite durch browsen, und das Addon zeigt an, welche CSS-Selektoren geladen werden, welche davon genutzt werden und welche nicht genutzt werden. Es findet sowohl inline-CSS als auch externe CSS-Dateien.
Alternativ kann man auch eine Sitemap angeben, die dann genutzt wird, um alle HTML-Seiten nach ungenutztem CSS zu durchsuchen.

Dann kann man sich speziell die nicht genutzten angucken, ob sie wirklich nicht mehr genutzt werden, und dann entfernen, um die CSS-Datei von Altlasten, Test-Selektoren usw zu befreien. Man sollte allerdings aufpassen beim Entfernen, es könnte ja sein, dass man einfach die eine Seite vergessen hat, wo der spezielle Selektor genutzt wird. Ich durchsuche auf jeden Fall noch meine Templates und View-Scripte nach dem Selector, bevor ich ihn lösche.

dustme0

dustme1

Mehr wollte ich eigentlich heute garnicht schreiben 😉

Written by Michael Kliewe

August 26th, 2009 at 9:28 am

Posted in PHP

Tagged with , , , ,

Externe Javascript Dateien zusammenfassen

with 30 comments

Wir alle wissen: Langsame Webseiten bekommen weniger Besucher. Dazu gibt es auch Auswertungen von großen Portalen und Suchmaschinen.

Doch wie beschleunigt man die eigene Webseite? Häufig übersieht man einen wichtigen Teil: Das Frontend bzw. den Client.

Mag das PHP-Script auch sehr schnell sein (die Zeit gemessen mit microtime() ergibt 0.1 Sekunden), die Seite kann sich trotzdem langsam anfühlen. Das liegt häufig an vielen externen Dateien, die der Browser noch nachladen muss. Dazu gehören sowohl CSS-Dateien, Javascript-Dateien, Bilder usw.

Weiterlesen »

Written by Michael Kliewe

August 25th, 2009 at 7:59 pm

Posted in PHP

Tagged with , , ,