Mittwoch, 13. August 2008

PHP Session Handler

Sessions sind ein sehr beliebtes Werkzeug um clientbasierende Informationen über viele Seitenaufrufe hinweg vom Server ohne größeren Aufwand abzurufen.

Hier solls jetzt nicht über Sessions allgemein gehen sondern darum, welche Möglichkeiten es gibt Sessions zu speichern.

Der PHP-Standard ist, dass die Sessions als normale Dateien in einem Ordner abgelegt werden.
das kann /tmp sein oder aber auch /var/www/sessions ...wie es beliebt.

Die Sessiondateien im Hauptspeicher zu behalten sollte effektiver sein, da die Festplatte beim Schreiben und Auslesen von Daten nicht beansprucht wird. Die Festplatte(n) ist(sind) nämlich oft ein Flaschenhals, besonders bei Datenbankservern. Auf vielen Servern laufen Web - und Datenbankserver gleichzeitig... da sollte jede Festplattenentlastung wahrgenommen werden.

Ich möchte nun kurz 2 Möglichkeiten vorstellen und deren Vor - und Nachteile nennen.

Die erste Möglichkeit ist der Session Handler "mm". Dieser ist verfügbar, wenn PHP mit "--with-mm" kompiliert wurde. (http://murksfurtz.blogspot.com/2008/08/schlankes-php-selbst-kompilieren.html)
Um diese Variante zu nutzen muss in der php.ini einfach die Variable "session.save_handler" von "files" auf "mm" geändert werden. PHP bzw. Webserver neustarten und die Sessions befinden sich nun im Arbeitsspeicher. Der Vorteil sollte klar sein => keine Festplattenlast durch Sessionhandling. Zudem ist diese Variante sehr einfach umzusetzen.
Nachteil: beim Webserver oder PHP-Neustart, sind die Sessiondaten verloren. Das heisst, User müssten sich in dem Fall neu einloggen.
Ein weiterer Nachteil: So ein Neustart hat zur Folge, dass die Sessiondaten auf dem Server gelöscht werden, die Cookies mit den Session-IDs auf den Clientrechnern aber noch vorhanden sind. Erstellt der Server nun neue Sessiondateien die zufällig die gleiche ID haben, wie einige noch vorhandene auf Clientrechnern, so kann es passieren, dass diese Zugriff auf andere Accounts bekommen... und das ist nicht so doll :)

Eine andere Möglichkeit bietet das Memcache-Modul. Um dieses zu nutzen, muss erst einmal "Memcached" installiert werden, welches ein Deamon unabhängig von PHP ist. Memcached gibts dort: http://www.danga.com/memcached/
Memcache ist nicht Teil des PHP Sources, man muss es daher nachträglich dynamisch oder statisch einbinden. Die Modulquellen gibts dort: http://pecl.php.net/package/memcache und wie man Extensions nachträglich statisch einbindet steht da: http://murksfurtz.blogspot.com/2008/08/php-modul-nachtrglich-statisch.html
Mit Memcache kann man übrigens, ähnlich wie mit Xcache, auch Variablen in den Speicher schreiben auf die jedes PHP Skript zugreifen kann.

So, wenn nun der Memcached Deamon läuft ändern wir in der php.ini den session.save_handler auf "memcache". Damit aber noch nicht genug. Wir müssen nun noch session.save_path ändern... und zwar sollte das dann so aussehen: session.save_path = "tcp://127.0.0.1:11211?persistent=1&weight=1&timeout=3&retry_interval=4" ...Memcached ist ja wie gesagt ein unabhängiges Programm und nutzt zur Kommunikation das TCP Protokoll. Ein Riesenvorteil der sich daraus ergibt ist, dass man von entfernten Rechnern aus, auf die Memcached-Daten zugreifen kann. Sehr interessant also im Falle von PHP-Loadbalancern, damit auch von verschiedenen Webservern aus, auf die selben Sessiondaten zugegriffen werden kann. Es fehlt allerdings eine Authentifizierungsmöglichkeit, so dass die Sicherheit über die Firewall hergestellt werden muss.

Eine 3. Möglichkeit ist das Speichern von Sessiondaten auf einer Ramdisk. Also einer Partition im Arbeitsspeicher, die wie eine Festplattenpartition behandelt wird. Dazu habe ich bereits was geschrieben, daher gibts an dieser Stelle nur noch einen Link: http://murksfurtz.blogspot.com/2008/08/ramdisk-mit-ramfs.html

Dienstag, 12. August 2008

Ramdisk mit ramfs

Tach,

wie man sich denken kann, sind Zugriffe auf den Arbeitsspeicher sehr viel schneller als Zugriffe auf die Festplatte.. warum also Dateien, auf die oft zugegriffen wird, nicht in den Arbeitsspeicher packen?
Kandidaten wären zum Beispiel der Ordner (oder Teile davon) der Http-Dateien oder aber auch die Session-Daten von PHP.

Dies geht sehr schön mit dem ramfs Dateisystem (ab Kernel 2.4).

Zuerst Vereichnis anlegen, dann Dateisystem mounten

# mkdir /RAM1
# /bin/mount -t ramfs none /RAM1 -o maxsize=10000

(das wars schon)

Nun haben wir eine Ramdisk mit 10MB Platz. Dieser Ordner lässt sich nun wie jeder andere Ordner auch behandeln. Wenn wir nun beispielsweise die PHP-Sessiondaten hier speichern wollen geben wir in der php.ini einfach session.save_path = "/RAM1" an.
"maxsize" gibt es laut Manpage übrigens nicht, soll aber wohl funktionieren (siehe Quellen) :)

Der Vorteil bei der Anwendung von ramfs liegt darin, dass - egal welche Größe man festlegt - nur so viel Speicher benutzt wird wie gerade benötigt. Also hat man nur 3MB Daten im obigen Beispiel, so benutzt das System die verbleibenden 7MB.

Wenn man die http Daten dort speichern möchte, muss man darauf achten, dass diese Daten bei einem Neustart des Rechners oder beim unmounten der Partition weg sind. Es lohnt sich also nur bei Dateien die nicht verändert werden oder unwichtig sind.

Ein Initskript, welches beim Systemstart ausgeführt werden kann um eine Ramdisk zu erstellen und den Http-Ordner dorthin zu kopieren, könnte so aussiehen:

#!/bin/sh
/bin/mount -t ramfs none /RAM1 -o maxsize=10000
/bin/cp -r -p /srv/www/htdocs /RAM1


schöner Artikel zum Thema: http://www.linuxfocus.org/Deutsch/July2001/article210.shtml

Samstag, 9. August 2008

XCache Variablen ODER: "superglobale Variablen"

So ganz kurze Einführung in den Variablenspeicher von XCache.

Mit dem Artikel http://murksfurtz.blogspot.com/2008/08/shmop-beispiel-fr-php.html hab ich ja schon mal aufgezeigt wie man das shmop Modul benutzen kann... nun ist dieses shmop nicht gerade sehr unkompliziert... allerdings sehr effizient, was den Speicherverbrauch angeht.

XCache ist dagegen um ein vielfaches einfacher anzuwenden. Nehmen wir wieder das Beispiel der Anzahl, der sich auf der Seite befindenen User. Normalerweise müsste (ohne Cache), jeder Client bei jedem Seitenaufruf die Datenbank belästigen um die Anzahl der eingeloggten User zu ermitteln.

Folgendes Skrip reicht schon aus um das ganze zu cachen:

//variable im cache vorhanden?
if (xcache_isset('user_online')) {
//wenn ja, hole den wert aus dem cache
$useronline = xcache_get('user_online');
}
//ansonsten hole wert aus datenbank und schreibe in den cache
else {
$result = mysql_query('SELECT COUNT(*) FROM blubb WHERE bedingung');
$daten = mysql_fetch_array($result);
$useronline = $daten[0];
//erstelle variable im cache, lebensdauer 30 sekunden
xcache_set('user_online', $useronline, 30);
}

So, nun wird die Datenbankabfrage maximal alle 30 Sekunden ausgeführt. Bei mehreren hundert oder tausend Nutzern online, erspart man MySQL schon etwas Arbeit.

XCache bietet im Übrigen noch ein paar interessante Funktionen. So lassen sich Variablen beispielsweise mit "xcache_inc()" hochzählen oder mit "xcache_dec()" herunterzählen.

XCache: http://xcache.lighttpd.net/

XCache statisch in PHP einbinden: http://murksfurtz.blogspot.com/2008/08/php-modul-nachtrglich-statisch.html

Mittwoch, 6. August 2008

shmop Beispiel für PHP

Damit ichs nicht vergesse und mich später nochmal damit beschäftigen kann, poste ich nun ein kleines shmop Beispiel. Habe es angewendet um die Anzahl der sich auf einer Internetseite befindenen Nutzer in einer Variablen zwischenzuspeichern. Diese Variable wird im RAM gehalten und jeder PHP Prozess kann auf diese Variable zugreifen. Das war nur ein erster Versuch mit diesem Modul. Ich werde später sicher noch einen dicken Beitrag zum Thema Caching bringen.


$jetzt = time();
//öffne zeitspeicher zum lesen
$shm_id = shmop_open(0xff2, "w", 0, 0);
//wenn nicht vorhanden, erstelle zeitspeicher und schreibe als zeit "1" rein
if (!$shm_id) {
$shm_id = shmop_open(0xff2, "c", 0644, 100);
shmop_write($shm_id, '1', 0);
}
//erstelle useronline speicher
$shm_id2 = shmop_open(0xff3, "w", 0, 0);
//wenn nicht vorhanden erstelle den speicher
if (!$shm_id2) {
$shm_id2 = shmop_open(0xff3, "c", 0644, 100);
}
//lese zeit aus dem zeitspeicher
$zeit_alt = shmop_read($shm_id, 0, 0);
//wenn zeitunterschied größer als 30 hole aktuelle useronline zahl und schreib userzahl und aktuelle zeit in den speicher
if ($jetzt - $zeit_alt > 30) {
$result = mysql_query('SELECT COUNT(*) FROM blubb WHERE bedingung');
$daten = mysql_fetch_array($result);
$online = $daten[0];
shmop_write($shm_id2, $online, 0);
shmop_write($shm_id, $jetzt, 0);
}
else {
$useronline = shmop_read($shm_id2, 0, 0);
}
shmop_close($shm_id);
shmop_close($shm_id2);

shmop: http://www.php.net/manual/de/book.shmop.php

böh... doofe Formatierung des Codes...


Dienstag, 5. August 2008

PHP-Modul nachträglich statisch einbinden

Nehmen wir an, wir haben eine tolle neue Erweiterung für PHP entdeckt und möchten diese ausprobieren. Wie bekannt ist, kann man diese "extensions" dynamisch oder statisch einbinden. Das nachträgliche Einbinden ist einfacher, wenn man es über den dynamischen Weg macht und eventuell auch sinnvoller, möchte man diese Erweiterung ersteinmal antesten.

Wir nehmen hierzu mal das XCache Modul. XCache ist ein opcode cacher. Ich werde auf das Thema in einem anderen Post nochmal eingehen. Aber kurz beschrieben: Jedes PHP Skript muss erst kompiliert werden, bevor es ausgeführt werden kann... Dieses Kompilieren findet bei jeder aufgerufenen PHP Datei statt. Hier greift ein opcode Cacher ein. Er speichert den kompilierten Code einfach in einem Zwischenspeicher, so dass bei den folgenden Aufrufen kein Kompilieren mehr notwendig ist. Ziemlich simpel, aber effektiv... daher ist es ein gutes Beispiel.

Um XCache nun als dynamische Erweiterung zu installieren geht man wie folgt vor:


#wget http://... (link zur quelle)
#tar xf xcache-*.tar.gz
#cd xcache
#phpize (oder phpize5)
#./configure --enable-xcache
#make
#make install
#cat xcache.ini >> /etc/php.ini

Dies sollte eine Datei namens xcache.so in das PHP Erweiterungsverzeichnis installiert haben. (nach "extension_dir" in der phpinfo gucken um das verzeichnis herauszufinden)
Apache bzw. PHP neustarten und das Modul sollte aktiv sein. Konfigurieren sollte es man natürlich auch noch in der php.ini.

Nun wollen wir aber - der besseren Performance wegen - das Modul statisch in PHP einbinden. Wie geht man da vor?

Wir wechseln in das "ext" unserers PHP Quellverzeichnisses... beispielsweise /root/php-5.2.6/ext/

#wget http://... (link zur quelle von xcache)
#tar xf xcache-*.tar.gz
#mv xcache-1.2.2 xcache
#cd xcache
#phpize (oder phpize5)

Nun gibts das verzeichnis /root/php-5.2.6/ext/xcache

Zurück ins Quellverzeichnis von PHP /root/php-5.2.6/

Wir müssen PHP nun zusammen mit der Erweiterung neu kompilieren (http://murksfurtz.blogspot.com/2008/08/schlankes-php-selbst-kompilieren.html)
dazu führen wir erstmal aus:

# make clean
# rm configure
# ./buildconf --force

nääät Fehlermeldung... jeht nich, weil unsere autoconf Version "zu neu" ist und diese mag PHP nicht (warum auch immer) ... also autoconf deinstallieren und die autoconf Version 2.1.3 installieren. Dies geht allerdings Problemlos (bei mir zumindest). Downloaden (http://www.gnu.org/software/autoconf/#downloading), entpacken, ./configure, make, make install, fertig.

jetzt nochmal

#./buildconf --force
#./configure --deine optionen... --enable-xcache
#make
#make install (vorher apache / php beenden)

Fertig. Wir haben das Modul nun statisch in PHP eingebunden. Falls in der php.ini noch keine Konfiguration für XCache vorhanden ist, einfach die Zeilen aus der xcache.ini aus dem xcache Quellpaket kopieren und in die php.ini einfügen.

XCache: http://xcache.lighttpd.net/

schlankes PHP selbst kompilieren

Ja so... 1. Thema... habe in den letzten Tagen PHP 5.2.6 auf mehreren Servern installieren müssen. Bisher nutzte ich immer die Standardpakete der Distributionen, doch jetzt erst ist mir aufgefallen, dass da sauviel qautsch mit bei ist, den wir niiieee verwendet haben. man dachte ja immer "ohhh hmhm 'pdo_mysql' ... klingt wichtig.. lass ich mal drin". Einen Überblick über die PECL Module von PHP findet man dort: http://www.php.net/manual/de/funcref.php ...wenn man sich nun anguckt, wozu diese ganzen Sachen gut sind, die in den standard rpm's und deb's drin sind, merkt man, dass man das meiste garnicht braucht.

Welche Erweiterungen man "nutzt", kann man übrigens mit folgendem Skript nachprüfen:

phpinfo();
?>

Den Code als phpinfo.php speichern und mitm Browser aufrufen.

Da stehen denn so sachen wie "pdo", "xml", "simplexml", "spl", "filter" und und und... alles Module, die Funktionen anbieten die wir bei uns nie verwendeten... also? Kann doch weg, oder? Klar, denn unnötige Funktionen machen unser schönes PHP doch so schwerfällig und wir wollen doch das Beste aus der Hardware holen.

Nun müssen wir PHP aber selber kompilieren... am besten erstmal php von der Kiste schmeissen. Bei OpenSuse geht das gut mit "yast" und der Softwareverwaltung. Ich beziehe mich in meinen Postings immer auf OpenSuse, da wir dieses standardmäßig verwenden. Bevor PHP deinstalliert wird aber bitte erstmal das configure command von der phpinfo sichern. (dieses ./configure --with...)
Zur Info: wir nutzen kein Apache... wer PHP mit Apache nutzt sollte PHP als Apachemodul installieren. Da ich das nie benötigte, gehe ich darauf auch nicht ein.. will ja nix falsches erzählen...glaube aber diese Sachen werden im configure command benötigt: --with-apxs and --with-apxs2

Nun ziehen wir uns die PHP Quellen.
Ab ins root oder Homeverzeichnis und

#wget http://link.zu.den.quellen.com/php-5.2.6.tar.gz

eingeben. (quellen findet man auf http://www.php.net/downloads.php)
danach entpacken mit

#tar xf php-5.*
#cd php-5*

Sooo mittlerweile müsste man ja wissen, welche Sachen man selbst für sein PHP benötigt... bei uns reichte folgendes configure vollkommen aus:

#./configure '--with-mysql=/usr/' '--with-mysqli' '--prefix=/usr' '--datadir=/usr/share/php5' '--mandir=/usr/share/man' '--with-libdir=lib64' '--includedir=/usr/include' '--sysconfdir=/etc/php5/fastcgi' '--with-config-file-path=/etc/php5/fastcgi' '--with-config-file-scan-dir=/etc/php5/conf.d' '--enable-session' '--with-mm' '--with-pcre-regex=/usr' '--disable-debug' '--enable-inline-optimization' '--disable-rpath' '--enable-shared' '--program-suffix=5' '--enable-force-cgi-redirect' '--disable-discard-path' '--enable-fastcgi' '--bindir=/usr/bin' '--disable-cli' '--disable-all' '--enable-ftp' '--with-gd' '--enable-mbstring' '--with-zlib' '--with-jpeg-dir=/usr/lib64' '--enable-shmop'

#./configure --help

Zeigt alle verfügbaren Optionen an. --enable-xxx nutzt man, wenn ein Modul keine externen Quellen/Bibliotheken benötigt. --with-xxx benutzt man beim Gegenteil.

Shmop benötigen auch die Wenigsten, daher können die Meisten das wohl rausnehmen. Schaut am besten selbst nach, was ihr benötigt.
Während configure arbeitet kann es zu diversen Fehlermeldungen kommen. Da man hier nun alles selbst kompiliert, benötigt man viele extra devel Pakete. Zum beispiel gd-devel für die gd Erweiterung oder mm-devel. Das kann etwas dauern bis man configure alle Wünsche erfüllt hat.
(hint: trotz erfolgreicher gd Installation, kann beispielsweise der jpeg Support fehlen, falls die jpeg Bibliotheken nicht installiert sind)
Zur Belohnung hat man ein schlankes PHP ohne externe Module, was auch einen Geschwindigkeitsgewinn bringt, da diese nicht nachträglich reingeladen werden müssen.

Wurde configure erfolgreich ausgeführt fehlen noch zwei Kommandos:

#make (kann etwas dauern)
#make install


tja... feddich. Die Binarys befinden sich nun in /usr/bin
Die datei heisst meines Wissens dann 'php-cgi5'.

So... das wars erstmal... wir haben nun ein schlankes PHP installiert. Wir nutzen dies übrigens mit dem spawn-fcgi Skript von lighttpd (www.lighttpd.net) (http://trac.lighttpd.net/trac/wiki/Docs%3AModFastCGI)

So was solln das hier und wer bin ich!?!?

ja ich bin so einer der sich tagtäglich mit php, mysql, linux und verschiedenen webservern beschäftigt. ich habe im laufe der jahre viel dazugelernt und manchmal möchte man seine erkenntnisse einfach nur so herausschreien... das tue ich nun mit diesem blog :)

achso... für welches projekt ich arbeite bleibt aus sicherheitsgründen natürlich geheim :)