Title: Apache-Tuning
Weitere Themen Apache 2.0 ist ein Allzweck-Webserver, der entworfen wurde, um ein ausgewogenes Maß an Flexibilität, Portierbarkeit und Leistungsfähigkeit zu bieten. Obwohl Apache 2.0 nicht speziell daraufhin ausgelegt wurde, neue Benchmark-Höchstwerte zu erreichen, ist er dennoch in der Lage, in vielen praktischen Einsatz- fällen eine hohe Performance zu liefern.
Im Vergleich mit der Apache-Version 1.3 enthält er viele zusätzliche Optimierungen, die den Durchsatz und die Skalierbarkeit erhöhen. Die meisten dieser Verbesserungen sind standardmäßig aktiviert. Für die Laufzeit- und Kompilerkonfiguration gibt es aber Auswahlmöglichkeiten, die sich spürbar auf die Leistungen auswirken. Im folgenden werden die Konfigurationsoptionen beschrieben, mit denen der Administrator eine Feinabstimmung bei der Apache 2.0-Installation vornehmen kann. Einige dieser Konfigurationsoptionen ermöglichen dem httpd-Server eine bessere Ausnutzung der Möglichkeiten der Hardware und des Betriebssystem,
während es andere dem Administrator erlauben, _auf_Kosten_von Funktionalität zusätzliche Geschwindigkeit zu erzielen.Aspekte der Hardware und des Betriebssystems Der zentrale Hardware-Aspekt für die Webserver-Leistung ist der Arbeitsspeicher. Ein Webserver sollte niemals dazu gezwungen sein, Hauptspeicherinhalte auf langsamere Speichermedien auslagern zu müssen, weil dies die Wartezeit für jede Anfrage in einen Bereich verschiebt, den der Benutzer nicht mehr als 'hinreichend schnell' empfinden wird. Der Anwender klickt dann auf 'Stop' und (danach auf) 'Neu laden', wodurch er die Serverbelastung noch erhöht. Sie können - und sollten - die Einstellung der Direktive
MaxClients so wählen, daß Ihr Server nicht so viele Kindprozesse startet, daß er dadurch beginnt, zu swappen. Das kann auf einfache Weise geschehen: Ermitteln Sie über die Prozessliste oder mit Hilfe eines Programms wietopdie Größe des durchschnittlichen Apache-Prozesses und teilen Sie die Größe des verfügbaren RAM durch diesen Wert (wobei Sie etwas Platz für andere Prozesse übrig lassen sollten).Der Rest ist banal: Sorgen Sie für eine ausreichend schnelle CPU, eine ausreichend schnelle Netzwerkkarte und hinreichend schnelle Festplatten. Was "ausreichend schnell" bedeutet, muss in der Praxis ausprobiert werden.
Die Auswahl des Betriebssystems ist meistenteils eine Frage lokaler Gegebenheiten. Einige Richtlinien haben sich aber als grundsätzlich nützlich erweisen:
Verwenden Sie die letzte stabile Version und die entsprechenden Patches des Betriebssystems Ihrer Wahl. Bei vielen Betriebssystemen wurden die TCP-Stacks und Thread-Bibliotheken in den letzten Jahren deutlich verbessert.
Wenn das Betriebssystem einen
sendfile(2)-Systemaufruf unterstützt, dann sorgen Sie dafür, dass die Version und/oder die Patches für dessen Aktivierung installiert sind. (Für Linux bedeutet das beispielsweise die Linux-Version 2.4 oder spätere Versionen. Frühe Solaris 8-Versionen benötigen möglicherweise einen Patch.) Bei Betriebssystemen, wo der Systemaufruf zur Verfügung steht, ermöglicht er dem Apache 2-Server eine schnellere Auslieferung statischer Inhalte bei einer niedrigeren CPU-Belastung.Aspekte der Laufzeitkonfiguration mod_dir mpm_common mod_status AllowOverride DirectoryIndex HostnameLookups EnableMMAP EnableSendfile KeepAliveTimeout MaxSpareServers MinSpareServers Options StartServers Suche nach Hostnamen und andere DNS-Aspekte Vor der Apache-Version 1.3 war die
HostnameLookups -Direktive standardmäßig aufOngesetzt. Das führt zu zusätzlicher Wartezeit für jede Anfrage, weil eine DNS-Suche durchgeführt werden muss, bevor die Anfrage abgeschlossen werden kann. Bei der Apache-Version 1.3 wird standardmäßig der WertOffgesetzt. Falls Sie in Ihren Protokolldateien eine Auflösung von IP-Adressen in Hostnamen benötigen, dann verwenden Sie dafür das Programmlogresolveder Apache-Distribution, eines der zahlreichen verfügbaren Programme zur Auswertung von Protokolldateien.Es ist zu empfehlen, diese Form der Nachbereitung der Protokolldateien auf einem anderen Rechner als dem eigentlichen Webserver durchzuführen, damit sie sich nicht auf die Leistungsfähigkeit des Servers auswirkt.
Wird eine der Anweisungen
oderAllow from domainverwendet (Verwendung eines Host- oder Domänennamens anstelle einer IP-Adresse), dann ist eine doppelt reverse DNS-Suche erforderlich (eine reverse gefolgt von einer vorwärts gerichteten Suche, um sicherzustellen, dass die reverse Suche nicht getäuscht wird). Um keine Leistungseinbußen hinnehmen zu müssen, sollten daher bei diesen Direktiven möglichst immer IP-Adressen anstatt Namen verwendet werden.Deny from domainDie Direktiven können auch auf einen Bereich beschränkt werden, beispielsweise auf einen
<Location /server-status>-Abschnitt. In diesem Fall werden die DNS-Suchen nur für Anfragen durchgeführt, für die die Kriterien zutreffen. Im folgenden Beispiel werden die Suchen außer für.html- und.cgi-Dateien deaktiviert:HostnameLookups off
<Files ~ "\.(html|cgi)$">
HostnameLookups on </Files>
Aber abgesehen von dieser Möglichkeit: Falls Sie DNS-Namen nur in einigen wenigen CGI-Anwendungen benötigen, sollten Sie in Erwägung ziehen, den
gethostbyname-Aufruf (nur) in denjenigen CGIs durchzuführen, die ihn benötigen.FollowSymLinks und SymLinksIfOwnerMatch Überall dort innerhalb Ihres URL-Raums, wo Sie a) die Option
Options FollowSymLinksnicht gesetzt oder b) die OptionOptions SymLinksIfOwnerMatchgesetzt haben, wird Apache zusätzliche Systemaufrufe benötigen, um eine Prüfung auf symbolische Links durchzuführen ( jeweils einen Aufruf für jede Komponente des Dateinamens). Ein Beispiel:DocumentRoot /www/htdocs
<Directory />
Options SymLinksIfOwnerMatch </Directory>
Erfolgt eine Anfrage nach dem URI
/index.html, ruft der Apachelstat(2)für/www,/www/htdocsund/www/htdocs/index.htmlauf. Die Ergebnisse dieser Aufrufe werden nicht zwischengespeichert; deshalb sind sie bei jeder weiteren Anfor derung erneut erforderlich. Soll wirklich eine Überprüfung der symbolischen Links stattfinden, kann dies auch wie folgt geschehen:DocumentRoot /www/htdocs
<Directory />
Options FollowSymLinks </Directory>
<Directory /www/htdocs>
Options -FollowSymLinks +SymLinksIfOwnerMatch </Directory>
Das verhindert zumindest zusätzliche Überprüfungen für den Pfad
DocumentRoot . Gibt esAlias - oderRewriteRule -Pfade außerhalb derDocumentRoot, müssen die entsprechenden Abschnitte hinzugefügt werden. Für höchste Leistungen ohne Überprüfung der symbolischen Links sollte immerFollowSymLinksund niemalsSymLinksIfOwnerMatchgesetzt werden.AllowOverride Wird im URL-Raum das Überschreiben erlaubt (normalerweise in den
.htaccess-Dateien), versucht der Apache für jede Komponente des Dateinamens, eine entsprechende (d. h. in dem Verzeichnis des bis dahin gebildeten Pfadnamens liegende).htaccess-Datei zu öffnen. Bei der AnweisungDocumentRoot /www/htdocs
<Directory />
AllowOverride all </Directory>
und einer Anfrage nach dem URI
/index.html, versucht der Apache die Dateien/.htaccess,/www/.htaccessund/www/htdocs/.htaccesszu öffnen. Die Lösung des Problems ist ähnlich wie fürOptions FollowSymLinks. Für höchste Leistungen sollte überall im DateisystemAllowOverride Noneverwendet werden.Content-Negotiation Falls möglich, sollte eine Content-Negotiation vermieden werden, wenn alle Möglichkeiten zur Leistungssteigerung ausgeschöpft werden sollen. Allerdings überwiegen in der Praxis die Vorteile der Content Negotiation gegenüber den Leistungseinbußen. Aber es gibt eine Situation, in denen der Server beschleunigt werden kann, ohne dabei auf Funktionalität verzichten zu müssen. Anstatt Wildcards wie
DirectoryIndex index zu benutzen, sollten Sie eine vollständige Optionsliste verwenden:
DirectoryIndex index.cgi index.pl index.shtml index.html wobei Sie den häufigsten Fall an erster Stelle angeben sollten.
Beachten Sie auch, daß das explizite Anlegen einer
type-map-Datei bessere Leistungen ermöglicht, als die Verwendung vonMultiViews, weil die erforderlichen Informationen durch Lesen einer einzigen Datei ermittelt werden können, anstatt das Verzeichnis nach Dateien durchsuchen zu müssen.Falls Ihre Site (unbedingt) Content Negotiation benötigt, sollten
type-map-Dateien anstelle der DirektiveOptions MultiViewsbenutzt werden. In der Beschreibung der Content-Negotiation werden die Methoden des Aushandelns und die Anweisungen zum Erzeugen vontype-map-Dateien ausführlich behandelt.Speicherzuordnungen In Situationen, in denen der Apache 2.0 auf den Inhalt einer auszuliefernden Datei betrachten muss (beispielsweise bei der Server-Side-Include-Verarbeitung) nimmt er normalerweise eine Speicherabbildung des Inhalts der Datei vor, wenn das Betriebssystem eine Form der Funktion
mmap(2)unterstützt.Bei einigen Betriebssystemen steigert die Speicherzuordnung die Leistung. Manchmal kann sie aber auch die Leistung mindern oder gar die Stabilität des Servers gefährden:
Bei einigen Betriebssystemen ist
mmapbei wachsender Anzahl von CPUs nicht so gut skalierbar wieread(2). Bei Solaris-Servern mit mehreren Prozessoren liefert der Apache 2.0 beispielsweise manchmal vom Server analysierte Dateien schneller aus, wennmmapdeaktiviert ist.Wird eine Speicherzuordnung für eine in einem eingebundenen NFS-Dateisystem befindliche Datei vorgenommen und ein Prozess eines anderer NFS-Client-Rechners löscht oder verkürzt die Datei, kann Ihr Prozeß einen Busfehler erzeugen, wenn er das nächste Mal versucht, auf den Inhalt der zugeordneten Datei zuzugreifen.
Bei Installationen, wo eine dieser Möglichkeiten zutrifft, sollte die Speicherzuordnung ausgelieferter Dateien mit
EnableMMAP offdeaktiviert werden. (Hinweis: Diese Direktive kann auf Verzeichnisebene überschrieben werden.)Sendfile In Situationen wo der Apache 2.0 den Inhalt der auszuliefernden Datei ignorieren kann (beispielsweise bei der Auslieferung des Inhalts statischer Dateien), wird normalerweise die
sendfile-Unterstützung des Servers genutzt, wenn das Betriebssystem diesendfile(2)-Operation unterstützt.Bei den meisten Betriebssystem verbessert die Verwendung von
sendfiledie Leistungen, weil separate Lese- und Sendemechanismen wegfallen. In bestimmten Fällen kann die Verwendung vonsendfilejedoch die Stabilität des httpd-Programms gefährden:
Bei manchen Betriebssystem ist die
sendfile-Unterstützung fehlerhaft, was vom build-System nicht erkannt wurde, insbesondere, wenn die ausführbaren Programmdateien auf einem anderen Rechner erstellt und auf einen Rechner mit fehlerhaftersendfile-Unterstützung übernommen wurden.Bei eingebundenen NFS-Dateien ist der Kernel möglicherweise nicht in der Lage, die Netzwerkdatei über den eigenen Zwischenspeicher zuverlässig zu bedienen.
Trifft einer dieser Umstände zu, sollte
sendfilemit der AnweisungEnableSendfile offfür die Auslieferung von Dateiinhalten deaktiviert werden. (Hinweis: Diese Direktive kann auf Verzeichnisebene überschrieben werden.)Prozesserzeugung Vor der Apache-Version 1.3 hatten die Einstellungen der Direktiven
MinSpareServers ,MaxSpareServers undStartServers drastische Auswirkungen auf die Ergebnisse von Vergleichstests. Insbesondere benötigte der Apache eine "Anfahrzeit", um eine ausreichende Anzahl von Kindprozessen für die Bewätigung der anfallenden Last zu erreichen. Nach der Initialisierung der mitStartServers angegebenen Anzahl von Prozessen, wurde nur ein Kindprozess pro Sekunde erzeugt, um die mitMinSpareServers angegebene Anzahl zu erreichen. Bei der Voreinstellung von5fürStartServers und bei 100 gleichzeitig zugreifenden Clients dauerte es 95 Sekunden, bis genug Kindprozesse gestartet waren. Das funktioniert in der Praxis, weil im normalen Betrieb nicht häufig Neustarts durchgeführt werden. Bei einem Vergleichstest, der sich nur über 10 Minuten erstreckt, führt das aber zu mageren Ergebnissen.Mit der Anzahl von einem Kindprozess pro Sekunde sollte vermieden werden, dass der Server in der Startphase mit diesem Vorgang überlastet wird. Wenn der Rechner damit beschäftigt ist, neue Kindprozesse zu starten, kann er andere Anfragen nicht bedienen. Aber die starken Auswirkungen auf die Leistung machte eine Abhilfe erforderlich. Mit der Apache-Version 1.3 trat der Sekundentakt etwas in den Hintergrund. Jetzt wird zuerst ein Prozess gestartet, eine Sekunde gewartet und dann zwei Prozesse gestartet. Nach einer weiteren Sekunde werden 4 Prozesse gestartet usw., bis 32 Kindprozesse pro Sekunde gestartet werden. Ist der mit
MinSpareServers angegebene Wert erreicht, wird der Vorgang abgebrochen.Das Antwortverhalten dieses Verfahrens scheint so gut zu sein, dass es meist unnötig ist, die Einstellungen für
MinSpareServers ,MaxSpareServers undStartServers zu verändern. Werden mehr als 4 Kindprozesse pro Sekunde gestartet, wird eine Meldung ins Fehlerprotokoll geschrieben. Taucht diese Meldung wiederholt auf, müssen die Einstellungen eventuell geändert werden. Die Ausgaben vonmod_status kann hier als Orientierung dienen.In Verbindung mit der Prozesserzeugung steht die Prozessbeendigung, die durch die
MaxRequestsPerChild -Einstellung ausgelöst wird. Die Standardeinstellung liegt bei0und bedeutet, dass es keine Obergrenze für die Anzahl der von einem Kindprozess bedienten Anfragen gibt. Liegt der Wert sehr niedrig, beispielsweise bei30, dann werden Sie ihn möglicherweise deutlich erhöhen wollen. Unter SunOS oder einer alten Version von Solaris sollte die Grenze bei zirka10000liegen, um Speicherlöcher zu vermeiden.Werden Keep-Alives verwendet, sind die Kindprozesse damit beschäftigt, nichts anderes zu tun, als auf Anfragen über die bereits geöffnete Verbindung zu warten. Die Voreinstellung für
KeepAliveTimeout mit15Sekunden, versucht, diesen Effekt zu mildern. Hier muss zwischen Netzwerkbandbreite und Serverressourcen abgewogen werden. Auf keinen Fall sollte der Wert bei über60Sekunden liegen, da sonst die meisten Vorteile verloren gehen.Beim Kompilieren zu berücksichtigende Aspekte Auswahl eines MPM Apache 2.x unterstützt die Auswahl mehrerer sogenannter Multi-Processing Module (MPMs). Ber der Übersetzung des Apache muss ein MPM ausgewählt werden. Für einige Betriebssysteme gibt es spezielle MPMs:
beos ,mpm_netware ,mpmt_os2 undmpm_winnt . Für allgemeinen Unix-Systeme gibt es mehrere MPMs, unter denen gewählt werden kann. Die Auswahl des MPM kann sich auf Geschwindigkeit und Anpassungsfähigkeit des Servers auswirken:
- Das
worker -MPM verwendet mehrere Kindprozesse mit jeweils vielen Threads. Jeder Thread bedient gleichzeitig eine Verbindung. Das Modul eignet sich gut für Server mit viel Datenverkehr, weil es weniger Arbeitsspeicher beansprucht alsprefork .- Das
prefork -MPM benutzt mehrere Kindprozesse mit jeweils einem Thread. Jeder Prozess bedient gleichzeitig eine Verbindung. Bei vielen Systemen ist dieses MPM hinsichtlich der Geschwindigkeit mit dem MPMworker vergleichbar, es beansprucht aber mehr Speicherplatz. Die geringe Thread-Anzahl ist in manchen Situationen vorteilhaft, insbesondere bei nicht Thread-sicheren Modulen anderer Hersteller. Außerdem ist die Fehlersuche bei Betriebssystemen mit schlechter Unterstützung für die Thread-Fehlersuche einfacher.Weiter Informationen zu diesen und anderen MPMs finden Sie in der MPM-Dokumentation.
Module Da die Speicherauslastung ein wichtiger Aspekt für die Leistungsfähigkeit ist, sollten Sie bestrebt sein, aktuell nicht benutzte Module zu eliminieren. Wenn die Module als DSOs eingerichtet wurden, muss lediglich die entsprechende
LoadModule -Direktive für dieses Modul auskommentiert werden. Dies erlaubt Ihnen, mit dem Entfernen von Modulen zu experimentieren und zu kontrollieren, ob Ihre Seite ohne diese Module noch funktioniert.Wurden Module dagegen statisch in den binären Apache-Server eingebunden, muss der Server erneut kompiliert werden, wenn Sie unerwünschte Module entfernen wollen.
In diesem Zusammenhang stellt sich natürlich die Frage, welche Module überhaupt benötigt werden und welche nicht. Die Antwort variiert selbstverständlich von Website zu Website. Eine minimale Modulliste enthält in der Regel die Module
mod_mime ,mod_dir undmod_log_config , wobei letzteres optional ist, da eine Website auch ohne Fehlerprotokollierung funktionsfähig ist. Dies wird allerdings nicht empfohlen.Unteilbare Operationen Einige Module wie z.B.
mod_cache und neuere Entwicklungs-Builds des MPMworkerbenutzen das unteilbare API der Apache Portable Runtime (APR). Dieses API stellt unteilbare Operationen zur Verfügung, mit denen eine einfache Thread-Synchronisation möglich ist.Standardmäßig implementiert die APR diese Operationen mit dem effizientesten Mechanismus des jeweiligen Betriebssystems. Viele moderne CPUs verfügen beispielsweise in der Hardware über die Kernfunktion Compare-and-Swap (CAS). Bei anderen Betriebssystemen verwendet die APR standardmäßig eine langsamere, Mutex-basierte Implementierung der unteilbaren API, um die Kompatibilität zu älteren CPU-Modellen zu gewährleisten, denen eine solche Anweisungen fehlen. Wird der Apache für eines dieser Betriebssysteme bei gleichzeitigem Einsatz neuerer CPUs eingerichtet, können Sie zur Übersetzungszeit eine schnellere Implementierung für die unteilbaren Operationen auswählen. Dies geschieht beim Kompilieren mit der Option
--enable-nonportable-atomics:./buildconf
./configure --with-mpm=worker --enable-nonportable-atomics=yesDie Option
--enable-nonportable-atomicsist für folgende Betriebssysteme relevant:
- Solaris auf SPARC
Standardmäßig verwendet die APR unter Solaris/SPARC Mutex-basierte unteilbare Operationen. Bei Angabe der Option--enable-nonportable-atomicserzeugt die APR jedoch Code, der einen SPARC v8plus-Opcode für schnelle Compare-and-Swap-Operationen auf Hardware-Basis verwendet. Wird der Apache mit dieser Option konfiguriert, sind die unteilbaren Operationen effektiver (geringere CPU-Auslastung und mehr gleichzeitige Zugriffe), das ausführbare Programm läuft aber nur mit UltraSPARC-CPUs.- Linux auf x86
Standardmäßig verwendet die APR unter Linux auf x86 Mutex-basierte unteilbare Operationen. Bei Angabe der Option--enable-nonportable-atomicserzeugt die APR jedoch Code, der einen 486-Opcode für schnelle Compare-and-Swap-Operationen auf Hardware-Basis verwendet. Das Ergebnis sind effizientere unteilbare Operationen, das ausführbare Programm läuft aber nur mit 486er CPUs oder Folgemodellen (nicht mit 386er CPUs).mod_status und ExtendedStatus On Wird beim Kompilieren und Ausführen des Apache-Servers
mod_status eingebunden und gleichzeitigExtendedStatus Ongesetzt, dann führt der Apache bei jeder Anfrage zwei Aufrufe vongettimeofday(2)odertimes(2)(je nach Betriebssystem) sowie mehrere zusätzliche Aufrufe vontime(2)(vor 1.3) durch. Das geschieht zur Protokollierung von Zeitangaben im Statusreport. Um beste Leistungen zu erzielen, sollteExtendedStatusaufoffgesetzt werden (was der Voreinstellung entspricht).accept-Serialisierung - Mehrere Sockets Achtung: Dieser Abschnitt wurde bezüglich der in Version 2.0 des Apache-HTTP-Servers durchgeführten Änderungen noch nicht vollständig aktualisiert. Ein Teil der (vorliegenden) Information mag weiterhin relevant sein, aber gehen Sie vorsichtig damit um.
Dieser Abschnitt befasst sich mit einem Mangel des Socket-API von Unix. Überwacht der Webserver mit mehreren
Listen -Anweisungen mehrere Ports oder Adressen, dann verwendet der Apacheselect(2), um jedes Socket auf eine bereite Verbindung hin zu überprüfen.select(2)zeigt an, ob für ein Socket null oder wenigstens eine wartende Verbindung vorliegt. Das Apache-Modell enthält mehrere Kindprozesse, wobei alle nicht beschäftigten Prozesse gleichzeitig auf neue Verbindungen abfragen. Eine naive Implementierung sieht etwa folgendermaßen aus (diese Beispiele entsprechen nicht dem Code, sie dienen lediglich der Veranschaulichung):for (;;) {
for (;;) { }
fd_set accept_fds; }
FD_ZERO (&accept_fds);
for (i = first_socket; i <= last_socket; ++i) {
FD_SET (i, &accept_fds); }
rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
if (rc < 1) continue;
new_connection = -1;
for (i = first_socket; i <= last_socket; ++i) {
if (FD_ISSET (i, &accept_fds)) { }
new_connection = accept (i, NULL, NULL); }
if (new_connection != -1) break;
if (new_connection != -1) break;
process the new_connection;
Bei dieser naiven Implementierung besteht die ernsthafte Gefahr, daß sich der Server in nutzlosen Berechnungen erschöpft. Mehrere Kindprozesse führen diese Schleife gleichzeitig aus, so dass auch mehrere Prozesse von
selectblockiert werden, wenn sie gerade keine Anfrage bearbeiten. Alle diese blockierten Kindprozesse werden reaktiviert und kehren vonselectzurück, wenn eine einzige Anfrage an einem beliebigen Socket erscheint (die Anzahl der Kindprozesse, die reaktiviert werden, hängt vom Betriebssystem und dre Zeitplanung ab). Jeder betritt die Schleife und versucht, die Verbindung zu akzeptieren (accept). Aber nur ein Prozess hat Erfolg (vorausgesetzt, nur eine Verbindung ist bereit), der Rest wird vonacceptblockiert. Dieses blockiert diese Kindprozesse tatsächlich derartig, daß sie nur noch Anforderungen von diesem einen Socket und keinem (der vielen) anderen bedienen können. Sie sitzen dort fest, bis genügend neue Anfragen für dieses Socket erscheinen, um sie alle zu reaktivieren. Dieses Problem wurde im PR#467 zum erstenmal dokumentiert. Es bieten sich mindestens zwei Lösungen an.Zum einen kann die Blockade durch die Sockets ausgeschlossen werden. In diesem Fall blockiert
acceptdie Kindprozesse nicht und sie dürfen sofort fortfahren. Dadurch wird aber CPU-Zeit vergeudet. Angenommen, Sie haben zehn unbeschäftigte Kindprozessen fürselectund nur eine eingehende Verbindung. Dann werden neun dieser Prozesse aktiviert und sie versuchen alle die Verbindung zu akzeptieren (accept), schlagen fehl und stehen wieder beiselect, ohne etwas zu verrichten. Zwischenzeitlich hat keiner dieser neun Prozesse Anfragen, die über andere Sockets eingehen, bedient. Insgesamt scheint dies keine glückliche Lösung zu sein, es sei denn, es gibt so viele wartende CPUs (in einem Multiprocessor-Rechner), wie es wartende Kindprozesse gibt, was eine sehr unwahrscheinliche Situation ist.Bei der zweiten Lösung, die der Apache benutzt, wird der Eintritt in die innere Schleife serialisiert. Die Schleife sieht folgendermaßen aus (die Unterschiede sind hervorgehoben):
for (;;) {
accept_mutex_on (); }
for (;;) {
fd_set accept_fds; }
FD_ZERO (&accept_fds);
for (i = first_socket; i <= last_socket; ++i) {
FD_SET (i, &accept_fds); }
rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
if (rc < 1) continue;
new_connection = -1;
for (i = first_socket; i <= last_socket; ++i) {
if (FD_ISSET (i, &accept_fds)) { }
new_connection = accept (i, NULL, NULL); }
if (new_connection != -1) break;
if (new_connection != -1) break;
accept_mutex_off ();
process the new_connection;
Die Funktionen
accept_mutex_onundaccept_mutex_offimplementieren eine wechselseitig ausschließende Semaphore. Nur ein Kindprozesse kann jeweils den Mutex zu einem gegebenen Zeitpunkt besitzen. Für die Implementierung der Mutexe stehen mehrere Möglichkeiten zur Verfügung. Die Auswahl ist in der Dateisrc/conf.h(bei Version 1.3) oder in der Dateisrc/include/ap_config.h(ab Version 1.3) definiert. Bei einigen Architekturen wurde keine Auswahl getroffen, so dass bei ihnen die Verwendung mehrererListen -Anweisungen nicht sicher ist.Mit der Direktive
AcceptMutex kann die gewählte Mutex-Implementierung während der Laufzeit geändert werden.
AcceptMutex flockDiese Methode verwendet den
flock(2)-Systemaufruf, um eine Sperrdatei zu blockieren (ihr Name wird mit derLockFile -Direktive definiert).AcceptMutex fcntlDiese Methode verwendet den
fcntl(2)-Systemaufruf, um eine Sperrdatei zu blockieren (ihr Name wird mit derLockFile -Direktive definiert).AcceptMutex sysvsem(Ab Version 1.3) Diese Methode verwendet Semaphoren im SysV-Stil, um den Mutex zu implementieren. Semaphoren im SysV-Stil haben leider unangenehme Nebeneffekte. Zum einen ist es möglich, dass der Apache beendet wird, ohne das die Semaphore vorher zurückgesetzt wird (siehe auch
ipcs(8)-Man-Page). Zum anderen ermöglicht das Semaphoren-API Denial-of-Service-Attacken durch CGI-Skripte zu, die unter der gleichen User-ID wie der Webserver ausgeführt werden (d.h.. alle CGI-Skripte, wenn nicht etwas ähnliches wiesuexecodercgiwrapperbenutzt wird). Aus diesen Gründen wird diese Methoden außer auf IRIX (wo die beiden zuvor genannten bei den meisten IRIX-Rechnern unverhältnismäßig aufwändig sind) von keiner anderen Architektur benutzt.AcceptMutex pthread(Ab Version 1.3) Diese Methode verwendet POSIX-Mutexe und sollte bei jeder Architektur funktionieren, die die vollständige POSIX-Thread-Spezifikation implementiert. Allerdings scheint sie nur unter Solaris (ab Version 2.5) zu funktionieren und auch dort nur bei bestimmten Konfigurationen. Wenn mit dieser Methode experimentiert wird, sollte darauf geachtet werden, ob der Server hängt und nicht mehr reagiert. Bei Servern mit ausschließlich statischen Inhalten kann sie gut funktionieren..
AcceptMutex posixsem(Ab Version 2.0) Diese Methode verwendet POSIX-Semaphoren. Die Semaphoreneigentümerschaft wird nicht wiederhergestellt, wenn ein Thread, der während des Prozesses den Mutex besitzt, einen Segmentfehler verursacht, was zum Hängen des Webservers führt.
Verwendet ein System eine hier nicht aufgeführte Methode für die Serialisierung, dann kann es sinnvoll sein, die APR um den ent- sprechenden Code zu erweitern.
Eine weitere Lösung, die aber niemals implementiert wurde, ist eine partielle Serialisierung der Schleife, was bedeutet, dass nur eine bestimmte Anzahl von Prozessen hinein gelassen wird. Das ist nur für Multiprozessor-Rechner interessant, wo mehrere Kindprozesse gleichzeitig ausgeführt werden können und die Serialisierung die volle Bandbreite eigentlich nicht ausnutzt. Dies ist ein mögliches Feld für zukünftige Bemühungen, wobei die Priorität niedrig ist, da hochgradig parallele Webserver nicht die Norm sind.
Idealerweise sollten Sie auf die Verwendung mehrfacher
Listen -Anweisungen verzichten, wenn gute Leistungen im Vordergrund stehen. Damit ist das Thema aber noch nicht abgeschlossen.accept-Serialisierung - Einzelnes Socket Die oben beschriebenen Verfahren eignen sich hervorragend für Server mit mehreren Sockets, aber wie sieht es bei Servern mit einem einzigen Socket aus? Rein theoretisch sollte keines der genannten Probleme auftreten, da alle Kindprozesse nur bis zum Ankommen der nächsten Verbindung in
accept(2)blockiert werden können. In der Praxis verbirgt sich aber dahinter fast das gleiche Verhalten, wie es für die nicht blockierende Lösung beschrieben wurde. Bei der Art und Weise, wie die meisten TCP-Stacks implementiert sind, aktiviert der Kernel nämlich alle inacceptblockierten Prozesse, wenn eine einzige Verbindung ankommt. Einer dieser Prozesse übernimmt die Verbindung und kehrt in den Benutzerbereich zurück, während der Übrigen im Kernel zirkulieren und sich selbst wieder 'schlafen legen', wenn sie bemerken, daß für sie keine zu bearbeitende Verbindung vorliegt. Dieses Zirkulieren ist im Anwendercode nicht zu erkennen, findet aber trotzdem statt und kann zum gleichen leistungsmindernden Verhalten führen wie die nicht blockierende Lösung bei mehreren Sockets.Es hat sich herausgestellt, dass viele Systeme sich gutmütiger verhalten, wenn auch im Fall eines einzelnen Socket eine Serialisierung vorgenommen wird. Dies entspricht in der Tat fast in allen Fällen dem Standardverhalten des Apache. Versuche unter Linux (Version 2.0.30 auf einem Rechner mit Dual Pentium pro 166 128Mb RAM) haben gezeigt, dass die Serialisierung im Fall des einzelnen Sockets zu einer Abnahme der Anfragen pro Sekunde von weniger 3% gegenüber der nicht serialisierten Variante geführt haben. Beim nicht serialisierten einzelnen Socket kamen aber pro Anfrage 100 ms Wartezeit hinzu. Bei DFÜ-Verbindungen ist diese Verzögerung höchstwahrscheinlich vernachlässigbar, also lediglich in schnellen LANs tatsächlich ein Problem. Wenn Sie die Serialisierung für den Betrieb mit nur einem Socket außer Kraft setzenwollen, können Sie die symbolische Konstante
SINGLE_LISTEN_UNSERIALIZED_ACCEPTdefinieren; in diesem Fall werden Server mit nur einem Socket nicht serialisierenSchleichender Verbindungsabbau Wie in Abschnitt 8 des Beitrags draft-ietf-http-connection-00.txt erörtert wird, muss ein HTTP-Server für eine zuverlässige Implementierung des Protokolls jede Richtung der Kommunikation unabhängig voneinander beenden (denn eine TCP-Verbindung ist bidirektional, wobei die beiden Teile voneinander unabhängig sind). Diese Tatsache wird von anderen Servern häufig übersehen, vom Apache wird sie aber seit der Version 1.2 korrekt implementiert.
Als diese Fähigkeit in Apache eingebaut wurde, sorgte sie aufgrund einer Kurzsichtigkeit (bei der gewählten Implementierung) für eine Reihe von Problemen auf verschiedenen Unix-Versionen. Die TCP-Spezifikation verlangt nicht, daß der Zustand
FIN_WAIT_2nur zeitlich begrenzt existieren darf, sie schließt dies jedoch auch nicht aus. Bei Systemen ohne timeout-Mechanismus verursachte der Apache 1.2 ein ewiges Hängen vieler Sockets imFIN_WAIT_2-Status. In vielen Fällen kann das durch ein Upgrade mit den aktuellen TCP/IP-Patches des Herstellers behoben werden. Bei Fällen, in denen der Hersteller nie entsprechende Patches freigegeben hat (beispielsweise SunOS4 - wobei Leute mit einer Source-Lizenz die entsprechenden Patches selbst durchführen können) haben wir uns entschieden, dieses Feature zu deaktivieren.Es gibt zwei Möglichkeiten, dies zu bewirken. Eine davon ist die (Verwendung der) Socket-Option
SO_LINGER. Leider wurde sie für die meisten TCP/IP-Stacks niemals korrekt implementiert. Selbst bei Stacks mit einer korrekten Implementierung (z. B. Linux 2.0.31) erweist sich dieses Verfahren als aufwändiger (CPU-Zeit) als die nachfolgend beschriebene Lösung.Zum größten Teil findet sich diese Apache-Implementierung in der Funktion
lingering_close(in der Dateihttp_main.c). Sie sieht ungefähr so aus:void lingering_close (int s)
{
char junk_buffer[2048]; }
/* shutdown the sending side */
shutdown (s, 1);
signal (SIGALRM, lingering_death);
alarm (30);
for (;;) {
select (s for reading, 2 second timeout); }
if (error) break;
if (s is ready for reading) {
if (read (s, junk_buffer, sizeof (junk_buffer)) <= 0) { }
break; }
/* just toss away whatever is here */
close (s);
Dadurch entsteht natürlicherweise ein gewisser Aufwand beim Beenden einer Verbindung, der jedoch für eine zuverlässige Implementierung unverzichtbar ist. Mit der zunehmenden Verbreitung von HTTP/1.1 und der (damit verbundenen) Persistenz aller Verbindungen, amortisiert sich dieser Aufwand mit zunehmender Menge von Anfragen. Wenn Sie mit dem Feuer spielen und dieses Feature deaktieren wollen, können Sie die Konstante
NO_LINGCLOSEdefinieren, aber davon wird grundsätzlich abgeraten. Je mehr die HTTP/1.1-Verbindungen zum Standard werden, um so unverzichtbarer wirdlingering_close(und Pipeline-Verbindungen sind schneller und sollten unterstützt werden).Scoreboard-Datei Die Apache-Prozesse und Kindprozesse kommunizieren über das sogenannte Scoreboard miteinander. Dieses sollte idealerweise im Shared Memory implementiert werden. Für diejenigen Betriebssysteme, zu denen wir selbst entweder Zugang haben oder für die uns eine detaillierte Portierung vorliegt, wird es normalerweise im Shared Memory implementiert. Bei den übrigen wird standardmäßi eine Datei auf der Festplatte verwendet. Eine solche Datei ist nicht nur langsam, sie ist auch unzuverlässiger. Überprüfen Sie die Datei
src/main/conf.hihres Systems und suchen Sie nach den DefinitionenUSE_MMAP_SCOREBOARDoderUSE_SHMGET_SCOREBOARD. Die Definition einer dieser beiden Konstanten (sowie des entsprechenden ihrer beiden GegenstückeHAVE_MMAPbzw.HAVE_SHMGET) bewirkt die Aktivierung des mitgelieferten Shared-Memory-Codes.Besitzt Ihr System einen andern Typ von Shared Memory, dann müssen Sie die Dateihttp_main.cbearbeiten und die entsprechenden Hooks hinzufügen, um ihn in Apache verwenden zu können.Historische Anmerkung: Die Portierung von Apache nach Linux verwendete erst seit der Version 1.2 Shared Memory. Diese Nachlässigkeit führte zu einen mangelhaften und unzuverlässigem Verhalten früherer Versionen von Apache für Linux. DYNAMIC_MODULE_LIMIT Sollten Sie nicht vorhaben, dynamisch nachladbare Module zu verwenden (was vermutlich der Fall sein wird, wenn Sie diese Zeilen lesen und versuchen, das letzte bißchen Performance aus ihrem Server herauszukitzeln), dann sollten Sie bei der Übersetzung des Servers
-DDYNAMIC_MODULE_LIMIT=0angeben. Dies spart RAM, der sonst nur für die Unterstützung dynamisch geladener Module reserviert werden müßte.Anhang: Ausführliche Analyse eines Trace Nachfolgend sehen Sie einen Trace der Systemaufrufe von Apache 2.0.38 unter Verwendung des Worker-MPM unter Solaris 8. Dieser Trace wurde durch das Kommando
truss -l -p httpd_child_pid erzeugt.truss -l -p httpd_child_pid. Die Option
-lweist das Programmtrussan, die ID des LWP (Lightweight Process - die Solaris-Variante eines Thread auf Kernelebene) zu protokollieren, der den Systemaufruf durchführt.Für andere Betriebssysteme gibt es entsprechende Programme wie zum Beispiel
strace,ktraceoderpar. Sie produzieren alle ähnliche Ausgaben.Bei diesem Trace hat ein Client eine 10 kByte große statische Datei vom Hauptprogramm httpd angefordert. Traces nicht statischer Anfragen oder Anfragen mit Content-Negotiation sehen ganz anders (und manchmal auch sehr unschön) aus.
/67: accept(3, 0x00200BEC, 0x00200C0C, 1) (sleeping...) /67: accept(3, 0x00200BEC, 0x00200C0C, 1) = 9In diesem Trace wird der Listener-Thread in LWP #67 ausgeführt.
Beachten Sie die fehlende accept(2)-Serialisierung. Bei diesem speziellen Betriebssystem verwendet derworker-MPM standardmäßig die nicht serialisierte Variante, wenn nicht mehrere Ports überwacht werden. /65: lwp_park(0x00000000, 0) = 0 /67: lwp_unpark(65, 1) = 0Beim Akzeptieren einer Verbindung, aktiviert der
listener-Thread einenworker-Thread für die Verarbeitung der Anfrage. In diesem Trace ist derworker-Thread für die Anfrage LWP #65 zugeordnet. /65: getsockname(9, 0x00200BA4, 0x00200BC4, 1) = 0Um die virtuellen Hosts implementieren zu können, muss der Apache die lokale Socket-Adresse, über welche diese Verbindung akzeptiert wurde. In vielen Situationen kann dieser Aufruf unterbleiben (wenn beispielsweise keine virtuellen Hosts vorhanden sind oder wenn
Listen -Direktiven benutzt werden, die keine Adressen mit Wildcards haben). Bisher wurde jedoch kein Versuch unternommen, Optimierungen in dieser Hinsicht durchzuführen. /65: brk(0x002170E8) = 0 /65: brk(0x002190E8) = 0Die
brk(2)-Aufrufe allokieren Speicher im Heap. Solche Aufrufe sind in einem Systemaufruf selten zu beobachten, weil dashttpd-Programm für die Verarbeitung der meisten Anfragen eigene Speicherallokatoren verwendet (apr_poolundapr_bucket_alloc). In diesem Trace wurde das Programmhttpdgerade gestartet, deshalb muss esmalloc(3)aufrufen, um die Speicherblöcke zu erhalten, aus denen es die eigenen Speicherallokatoren erzeugt. /65: fcntl(9, F_GETFL, 0x00000000) = 2 /65: fstat64(9, 0xFAF7B818) = 0 /65: getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B910, 2190656) = 0 /65: fstat64(9, 0xFAF7B818) = 0 /65: getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B914, 2190656) = 0 /65: setsockopt(9, 65535, 8192, 0xFAF7B918, 4, 2190656) = 0 /65: fcntl(9, F_SETFL, 0x00000082) = 0Als nächstes setzt der
worker-Thread die Verbindung zum Client (Dateideskriptor 9) in den nicht blockierenden Modus. Diesetsockopt(2)- undgetsockopt(2)-Aufrufe sind ein Nebeneffekt davon, wielibcvon Solarisfcntl(2)für Sockets handhabt. /65: read(9, " G E T / 1 0 k . h t m".., 8000) = 97Der
worker-Thread liest die Anfrage des Clients./65: stat("/var/httpd/apache/httpd-8999/htdocs/10k.html", 0xFAF7B978) = 0 /65: open("/var/httpd/apache/httpd-8999/htdocs/10k.html", O_RDONLY) = 10Dieses
httpd-Programm wurde mitOptions FollowSymLinksundAllowOverride Nonekonfiguriert. Daher ist es für jedes Verzeichnis im Pfad zur angeforderten Datei weder erforderlich, einenlstat(2)-Aufruf durchzuführen (um auf symbolische Links zu testen), noch, es nach (= auf die Existenz) einer.htaccess-Datei zu prüfen. Es wird lediglichstat(2)aufgerufen, um zu überprüfen, dass: 1. die Datei existiert und 2. eine reguläre Datei und kein Verzeichnis ist. /65: sendfilev(0, 9, 0x00200F90, 2, 0xFAF7B53C) = 10269In diesem Beispiel ist das
httpd-Programm in der Lage, den HTTP-Antwort-Header und die angeforderte Datei mit einem einzigensendfilev(2)-Systemaufruf zu versenden. dieSendfile-Semantik variiert bei den unterschiedlichen Betriebssystemen. Bei einigen muss vor dem Aufruf vonsendfile(2)einwrite(2)- oder einwritev(2)-Aufruf erfolgen, um die Header zu senden. /65: write(4, " 1 2 7 . 0 . 0 . 1 - ".., 78) = 78Diesen
write(2)-Aufruf zeichnet die Anfrage im Zugriffsprotokoll auf. Beachten Sie, daß in diesem Trace unter anderem keintime(2)-Aufruf auftaucht. Anders als Apache 1.3 verwendet Apache 2.0gettimeofday(3), um die Zeit zu ermitteln. Einige Betriebssystem wie Linux oder Solaris besitzen eine optimierte Implementierung vongettimeofday, die nicht soviel zusätzlichen Aufwand wie ein normaler Systemaufruf erfordert. /65: shutdown(9, 1, 1) = 0 /65: poll(0xFAF7B980, 1, 2000) = 1 /65: read(9, 0xFAF7BC20, 512) = 0 /65: close(9) = 0Der
worker-Thread schließt die Verbindung mitlingering_close. . /65: close(10) = 0 /65: lwp_park(0x00000000, 0) (sleeping...)Am Ende schließt der
worker-Thread die Datei, die er gerade ausgeliefert hat und blockiert, bis der Listener ihm eine andere Verbindung zuweist. /67: accept(3, 0x001FEB74, 0x001FEB94, 1) (sleeping...)In der Zwischenzeit ist der Listener-Thread in der Lage, eine andere Verbindung anzunehmen, wenn er diese Verbindung einem
worker-Thread zugeteilt hat (aufgrund der Kontrollflußlogik desworker-MPM , der den Listener bremst, wenn die verfügbarenworker-Threads alle beschäftigt sind). Auch wenn das in diesem Trace nicht ersichtlich ist, kann der nächsteaccept(2)-Aufruf parallel mit der Behandlung der gerade vomworker-Thread akzeptierten Verbindung durchgeführt werden (was normalerweise bei starker Belastung auch geschieht).--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
