Nachdem der Webserver Apache im April 2002 die stabile Version 2 erreicht hat, biegt nun auch Mod_perl 2 in die Zielgerade ein[1]. Wer das Modul nicht kennt, denkt vielleicht, es handle sich um ein Pendant für PHP oder CGI. Das ist zwar nicht falsch, doch »mod_perl« ist wesentlich mächtiger. Webentwickler können damit in alle Phasen der Bearbeitung einer Anfrage eingreifen und Dinge tun, die mit CGI-Skripten oder Ähnlichem unmöglich sind.
Aufpasser und Arbeiter
Apache 2 benutzt mit seinen Multi-Processing-Modulen (MPM) ein Arbeitsmodell, das auf Prozesse oder auf Threads setzt[2]. Der Server arbeitet nach einem Supervisor-Worker-Prinzip: Direkt nach dem Start ist nur der Supervisor aktiv. Er liest die Konfigurationsdatei und startet dann eine Reihe so genannter Worker. Bei ihnen handelt es sich um Prozesse (Prefork MPM) oder Threads (Threaded MPM). Neben reinen Varianten gibt es auch Mischformen.
Nach der Konfiguration überwacht der Supervisor die einzelnen Worker, die eingehende Webrequests bearbeiten. Er startet und beendet Worker nach Bedarf. Abbildung 1 veranschaulicht das Modell und zeigt mögliche Eingriffspunkte für Apache-Perl-Programmierer. Der Artikel bezieht sich im Folgenden auf das Prefork-MPM und Mod_perl-2.0RC3. Geringfügige Änderungen am API sind bis zur endgültigen Version möglich, aber ziemlich unwahrscheinlich.
Jede Phase stellt einen bestimmten Abschnitt der Aktivität des Servers oder in der Bearbeitung einer Anfrage dar. Module können Handler für diese Phasen registrieren, die Apache zum entsprechenden Zeitpunkt abarbeitet. Konfigurationsanweisungen nach dem Muster »Perl NameHandler« weisen Mod_perl dazu an, in den entsprechenden Phasen Funktionen auszuführen. Dabei ersetzt man » Name« durch den entsprechenden Phasennamen. Beispielsweise registriert ein »PerlOpenLogsHandler« in der Apache-Konfiguration eine Funktion für die »OpenLogs«-Phase.
Direkt nach dem Start liest der Apache-Server die Konfiguration ein. Der Perl-Interpreter startet normalerweise am Anfang der »OpenLogs«-Phase. Es gibt allerdings zwei Fälle, in denen er früher startet: bei einem Block »<Perl>...</Perl>« in der Konfigurationsdatei sowie bei einer »PerlLoadModule«-Anweisung. Im Anschluss an die »PostConfig«-Phase startet Apache neu, um zu testen, ob ein Restart auch zu einem späteren Zeitpunkt erfolgreich wäre. Er durchläuft die »OpenLogs«- und »PostConfig«-Abschnitte also mehrmals.
Normalerweise öffnen Module in der »OpenLogs«-Phase ihre Logfiles. Auch Apache selbst beginnt dann damit, seine Logfiles »TransferLog« und »ErrorLog« zu schreiben. Über die »PerlOpenLogsHandler«-Anweisung können Perl-Module hier zum ersten Mal in den Ablauf eingreifen.
Konfiguration ändern
Danach tritt Apache in die »PostConfig«-Phase ein. In ihr ist sein Konfigurationsbaum bereits vollständig eingelesen. Hier kann ein Perl-Modul also auch auf die Konfiguration anderer Module zugreifen. Der in Listing 1 gezeigte Ausschnitt aus der Apache-Konfiguration fügt den »ServerTokens« den Wert der »User«-Anweisung hinzu. Diese Zeilen gehören am besten in eine separate Konfigurationsdatei, die Apache beim Starten liest, zum Beispiel in die »/etc/httpd/conf.d/perltest.conf«.
Damit das Beispiel funktioniert, muss die Konfigurationsvariable »ServerTokens« auf »Full« gesetzt sein (Zeile 12). Viele Distributionen setzen diese Direktive auf »OS«, um nicht mehr Informationen preiszugeben als nötig. Nach dem Ausprobieren sollte man also wieder den Defaultwert einstellen. Gibt es Probleme mit dem Beispiel, hilft der Kasten "Fehlersuche und Module" weiter.
Die Auswirkung dieser Konfiguration zeigt ein Aufruf mit dem Kommandozeilen-Client »curl«. Die Option »-I« sorgt dafür, dass das Programm die HTTP- Header ausgibt:
> curl -I http://localhost
HTTP/1.1 200 OK
Date: Sun, 16 Jan 2005 12:10:28 GMT
Server: Apache/2.0.52 (Linux/SUSE)
User/wwwrun mod_ssl/2.0.52
...
Mit dem Beispiel-Handler zeigt Apache nun auch den Benutzernamen an, den der Server annimmt, hier »wwwrun«. Ansonsten läuft der Code in dieser Phase mit der User-ID, mit der Apache gestartet wurde, also meist Root.
Abbildung 1: Die Supervisor-Komponente von Apache startet mehrere Worker. Sie laufen bis zu ihrem Lebensende in einem Zyklus und bedienen Webrequests.
Apache kennt noch eine »PreConfig«-Phase, die jedoch mit Perl nicht nutzbar ist, da zu dieser Zeit noch kein Perl-Interpreter läuft. Während die beiden bisher behandelten Abschnitte Sache des Supervisors sind, betreffen alle anderen nur die Worker.
|
Mod_perl richtet sich nach dem objektorientierten Modell. Allerdings hat es einige Eigenheiten, die das Beispiel der »Apache::RequestRec«-Klasse demonstriert. So werden alle Handler des HTTP-Anfragezyklus mit einem Objekt dieser Klasse als Parameter aufgerufen. Das Modul enthält aber nur die Basisklasse. Für mehr Funktionalität muss das Perl-Skript weitere Module laden. Diese erzeugen aber keine neuen Klassen, sondern erweitern »Apache ::RequestRec«. So verhält es sich auch mit anderen Mod_perl-Klassen. Einige Beispiele für solche Erweiterungsmodule sind »Apache::Filter« und »Apache::RequestUtil«.
Dieser Ansatz hat einige praktische Auswirkungen bei der Fehlersuche, wenn zum Beispiel im »error_log« eine Zeile ähnlich der folgenden auftaucht:
[client 127.0.0.1] Can't locate object
method "dir_config" via package
"Apache::RequestRec" at
/etc/httpd/conf.d/perltest.conf line 7
Das Perl-Modul findet offensichtlich die Methode »dir_config« nicht, obwohl das Modul »Apache::RequestRec« eingebunden ist. Der Grund ist, dass das Erweiterungsmodul »Apache::RequestUtil« die Methode implementiert.
Methoden finden mit Perl Mod_perl hilft mit einem Modul dabei, Methoden zu finden:»ModPerl::MethodLookup«, das die Funktion»print_method« implementiert. So sucht folgendesKommando nach der fehlenden »dir_config«-Methode:
perl -MApache2 -MModPerl::MethodLookup
-e print_method dir_config
Dieses Konstrukt findet die gesuchten Methoden. Ein Bash-Alias vereinfacht den Prozess nochmals:
alias lookup="perl -MApache2
-MModPerl::MethodLookup -e print_method"
Nun genügt zur Suche »lookup Name«:
> lookup dir_config
There is more than one class with method
'dir_config'
try one of:
use Apache::RequestUtil ();
use Apache::ServerUtil ();
Das Ergebnis: »mod_perl« findet zwei Module, die eine Methode dieses Namens implementieren. Um zu entscheiden, welche die richtige ist, hilft ein Blick in die Dokumentation des Moduls - je nach Installation mit »perldoc«, »mp2doc« oder »man«.
Die passende Dokumentation ist selbstverständlich auch auf der Webseite von Mod_perl zu finden. Der Modulname ist in diesem Fall hinter der Adresse [http://perl.apache.org/docs/2.0/api/] einzutragen. Allerdings sind dabei die Doppelpunkte »::« zwischen den Klassennamen durch einen Schrägstrich »/« zu ersetzen, also zum Beispiel für das Modul »Apache::RequestUtil«: [http://perl.apache.org/docs/2.0/api/Apache/RequestUtil]
|
Bevor ein Worker in den Anfragezyklus eintritt, bearbeitet er die »ChildInit«-Phase (Konfigurationsanweisung »PerlChildInitHandler«). Da jeder Worker diese Phase nur einmal durchläuft, eignet sie sich zum Belegen globaler Ressourcen wie etwa Datenbankverbin-dungen. »ChildInit« ist die erste Gelegenheit, Perl-Code mit der richtigen Serveridentität auszuführen: Der Server hat »User«- und »Group«-Anweisungen dann schon abgearbeitet.
Hat ein Worker den Anfragezyklus beendet, tritt er in die »ChildExit«-Phase ein (Konfiguration: »PerlChildExitHandler«). Sie bietet sich an, um globale Ressourcen wieder freizugeben. In diesen Zustand geht ein Worker übrigens nicht nur beim Beenden von Apache über: Mit der Anweisung »MaxRequestsPerChild« kann (und sollte) der Programmierer die Anzahl der Requests, die ein einzelner Worker bearbeitet, begrenzen.