<-
Apache > Serveur HTTP > Documentation > Version 2.4 > Documentations diverses

Optimisation des performances d'Apache

Langues Disponibles:  en  |  fr  |  ko  |  tr 

Apache 2.x est un serveur web ´┐Ż usage g´┐Żn´┐Żral, con´┐Żu dans un but d'´┐Żquilibre entre souplesse, portabilit´┐Ż et performances. Bien que non con´┐Żu dans le seul but d'´┐Żtablir une r´┐Żf´┐Żrence en la mati´┐Żre, Apache 2.x est capable de hautes performances dans de nombreuses situations du monde r´┐Żel.

Compar´┐Że ´┐Ż Apache 1.3, la version 2.x comporte de nombreuses optimisations suppl´┐Żmentaires permettant d'am´┐Żliorer le d´┐Żbit du serveur et sa personnalisation. La plupart de ces am´┐Żliorations sont activ´┐Żes par d´┐Żfaut. Cependant, certains choix de configuration ´┐Ż la compilation et ´┐Ż l'ex´┐Żcution peuvent affecter les performances de mani´┐Żre significative. Ce document d´┐Żcrit les options qu'un administrateur de serveur peut configurer pour am´┐Żliorer les performances d'une installation d'Apache 2.x. Certaines de ces options de configuration permettent au d´┐Żmon httpd de mieux tirer parti des possibilit´┐Żs du mat´┐Żriel et du syst´┐Żme d'exploitation, tandis que d'autres permettent ´┐Ż l'administrateur de privil´┐Żgier la vitesse par rapport aux fonctionnalit´┐Żs.

top

Probl´┐Żmes mat´┐Żriels et relatifs au syst´┐Żme d'exploitation

Le principal probl´┐Żme mat´┐Żriel qui affecte les performances du serveur web est la m´┐Żmoire vive (RAM). Un serveur web ne devrait jamais avoir ´┐Ż utiliser le swap, car le swapping augmente le temps de r´┐Żponse de chaque requ´┐Żte au del´┐Ż du point que les utilisateurs consid´┐Żrent comme "trop lent". Ceci incite les utilisateurs ´┐Ż cliquer sur "Stop", puis "Charger ´┐Ż nouveau", ce qui a pour effet d'augmenter encore la charge du serveur. Vous pouvez, et m´┐Żme devez d´┐Żfinir la valeur de la directive MaxRequestWorkers de fa´┐Żon ´┐Ż ce que votre serveur ne lance pas un nombre de processus enfants tel qu'il commence ´┐Ż faire du swapping. La m´┐Żthode pour y parvenir est simple : d´┐Żterminez la taille de votre processus Apache standard en consultant votre liste de processus ´┐Ż l'aide d'un outil tel que top, et divisez votre quantit´┐Ż totale de m´┐Żmoire disponible par cette taille, tout en gardant un espace suffisant pour les autres processus.

Hormis ce r´┐Żglage relatif ´┐Ż la m´┐Żmoire, le reste est trivial : le processeur, la carte r´┐Żseau et les disques doivent ´┐Żtre suffisamment rapides, o´┐Ż "suffisamment rapide" doit ´┐Żtre d´┐Żtermin´┐Ż par l'exp´┐Żrience.

Le choix du syst´┐Żme d'exploitation d´┐Żpend principalement du contexte local. Voici cependant quelques conseils qui se sont g´┐Żn´┐Żralement av´┐Żr´┐Żs utiles :

top

Optimisation de la configuration ´┐Ż l'ex´┐Żcution

HostnameLookups et autres consid´┐Żrations ´┐Ż propos du DNS

Avant Apache 1.3, la directive HostnameLookups ´┐Żtait positionn´┐Że par d´┐Żfaut ´┐Ż On. Ce r´┐Żglage augmente le temps de r´┐Żponse de chaque requ´┐Żte car il entra´┐Żne une recherche DNS et le traitement de la requ´┐Żte ne pourra pas ´┐Żtre achev´┐Ż tant que cette recherche ne sera pas termin´┐Że. Avec Apache 1.3, ce r´┐Żglage est d´┐Żfini par d´┐Żfaut ´┐Ż Off. Si vous souhaitez que les adresses dans vos fichiers journaux soient r´┐Żsolues en noms d'h´┐Żtes, utilisez le programme logresolve fourni avec Apache, ou un des nombreux paquets g´┐Żn´┐Żrateurs de rapports sur les journaux disponibles.

Il est recommand´┐Ż d'effectuer ce genre de traitement a posteriori de vos fichiers journaux sur une autre machine que celle qui h´┐Żberge le serveur web en production, afin que cette activit´┐Ż n'affecte pas les performances du serveur.

Si vous utilisez une directive Allowfrom domain ou Deny from domain (ce qui signifie que vous utilisez un nom d'h´┐Żte ou un nom de domaine ´┐Ż la place d'une adresse IP), vous devrez compter avec deux recherches DNS (une recherche inverse suivie d'une recherche directe pour s'assurer que l'adresse IP n'a pas ´┐Żt´┐Ż usurp´┐Że). C'est pourquoi il est pr´┐Żf´┐Żrable, pour am´┐Żliorer les performances, d'utiliser des adresses IP plut´┐Żt que des noms lorsqu'on utilise ces directives, du moins chaque fois que c'est possible.

Notez qu'il est possible de modifier la port´┐Że des directives, en les pla´┐Żant par exemple ´┐Ż l'int´┐Żrieur d'une section <Location /server-status>. Les recherches DNS ne seront alors effectu´┐Żes que pour les requ´┐Żtes qui satisfont aux crit´┐Żres. Voici un exemple qui d´┐Żsactive les recherches DNS sauf pour les fichiers .html et .cgi :

HostnameLookups off
<Files ~ "\.(html|cgi)$">
  HostnameLookups on
</Files>

Mais m´┐Żme dans ce cas, si vous n'avez besoin de noms DNS que dans certains CGIs, vous pouvez effectuer l'appel ´┐Ż gethostbyname dans les CGIs sp´┐Żcifiques qui en ont besoin.

FollowSymLinks et SymLinksIfOwnerMatch

Chaque fois que la ligne Options FollowSymLinks sera absente, ou que la ligne Options SymLinksIfOwnerMatch sera pr´┐Żsente dans votre espace d'adressage, Apache devra effectuer des appels syst´┐Żme suppl´┐Żmentaires pour v´┐Żrifier la pr´┐Żsence de liens symboliques. Un appel suppl´┐Żmentaire par ´┐Żl´┐Żment du chemin du fichier. Par exemple, si vous avez :

DocumentRoot /www/htdocs
<Directory />
  Options SymLinksIfOwnerMatch
</Directory>

et si une requ´┐Żte demande l'URI /index.html, Apache effectuera un appel ´┐Ż lstat(2) pour /www, /www/htdocs, et /www/htdocs/index.html. Les r´┐Żsultats de ces appels ´┐Ż lstat ne sont jamais mis en cache, ils devront donc ´┐Żtre g´┐Żn´┐Żr´┐Żs ´┐Ż nouveau pour chaque nouvelle requ´┐Żte. Si vous voulez absolument v´┐Żrifier la s´┐Żcurit´┐Ż des liens symboliques, vous pouvez utiliser une configuration du style :

DocumentRoot /www/htdocs
<Directory />
  Options FollowSymLinks
</Directory>

<Directory /www/htdocs>
  Options -FollowSymLinks +SymLinksIfOwnerMatch
</Directory>

Ceci ´┐Żvite au moins les v´┐Żrifications suppl´┐Żmentaires pour le chemin d´┐Żfini par DocumentRoot. Notez que vous devrez ajouter des sections similaires si vous avez des chemins d´┐Żfinis par les directives Alias ou RewriteRule en dehors de la racine de vos documents. Pour am´┐Żliorer les performances, et supprimer toute protection des liens symboliques, ajoutez l'option FollowSymLinks partout, et n'utilisez jamais l'option SymLinksIfOwnerMatch.

AllowOverride

Dans toute partie de votre espace d'adressage o´┐Ż vous autoriserez la surcharge de la configuration (en g´┐Żn´┐Żral ´┐Ż l'aide de fichiers .htaccess), Apache va tenter d'ouvrir .htaccess pour chaque ´┐Żl´┐Żment du chemin du fichier demand´┐Ż. Par exemple, si vous avez :

DocumentRoot /www/htdocs
<Directory />
  AllowOverride all
</Directory>

et qu'une requ´┐Żte demande l'URI /index.html, Apache tentera d'ouvrir /.htaccess, /www/.htaccess, et /www/htdocs/.htaccess. Les solutions sont similaires ´┐Ż celles ´┐Żvoqu´┐Żes pr´┐Żc´┐Żdemment pour Options FollowSymLinks. Pour am´┐Żliorer les performances, utilisez AllowOverride None pour tous les niveaux de votre espace d'adressage.

N´┐Żgociation

Dans la mesure du possible, ´┐Żvitez toute n´┐Żgociation de contenu si vous tenez au moindre gain en performances. En pratique toutefois, les b´┐Żn´┐Żfices de la n´┐Żgociation l'emportent souvent sur la diminution des performances. Il y a cependant un cas dans lequel vous pouvez acc´┐Żl´┐Żrer le serveur. Au lieu d'utiliser une directive g´┐Żn´┐Żrique comme :

DirectoryIndex index

utilisez une liste explicite d'options :

DirectoryIndex index.cgi index.pl index.shtml index.html

o´┐Ż vous placez le choix courant en premi´┐Żre position.

Notez aussi que cr´┐Żer explicitement un fichier de correspondances de type fournit de meilleures performances que l'utilisation des MultiViews, car les informations n´┐Żcessaires peuvent ´┐Żtre simplement obtenues en lisant ce fichier, sans avoir ´┐Ż parcourir le r´┐Żpertoire ´┐Ż la recherche de types de fichiers.

Par cons´┐Żquent, si la n´┐Żgociation de contenu est n´┐Żcessaire pour votre site, pr´┐Żf´┐Żrez les fichiers de correspondances de type aux directives Options MultiViews pour mener ´┐Ż bien cette n´┐Żgociation. Se r´┐Żf´┐Żrer au document sur la N´┐Żgociation de contenu pour une description compl´┐Żte des m´┐Żthodes de n´┐Żgociation, et les instructions permettant de cr´┐Żer des fichiers de correspondances de type.

Transfert en m´┐Żmoire

Dans les situations o´┐Ż Apache 2.x doit consulter le contenu d'un fichier en train d'´┐Żtre servi - par exemple ´┐Ż l'occasion du traitement d'une inclusion c´┐Żt´┐Ż serveur - il transf´┐Żre en g´┐Żn´┐Żral le fichier en m´┐Żmoire si le syst´┐Żme d'exploitation supporte une forme quelconque de mmap(2).

Sur certains syst´┐Żmes, ce transfert en m´┐Żmoire am´┐Żliore les performances. Dans certains cas, ce transfert peut toutefois les d´┐Żgrader et m´┐Żme diminuer la stabilit´┐Ż du d´┐Żmon httpd :

Pour les installations o´┐Ż une de ces situations peut se produire, vous devez utiliser EnableMMAP off afin de d´┐Żsactiver le transfert en m´┐Żmoire des fichiers servis. (Note : il est possible de passer outre cette directive au niveau de chaque r´┐Żpertoire.)

Sendfile

Dans les cas o´┐Ż Apache peut se permettre d'ignorer le contenu du fichier ´┐Ż servir - par exemple, lorsqu'il sert un contenu de fichier statique - il utilise en g´┐Żn´┐Żral le support sendfile du noyau si le syst´┐Żme d'exploitation supporte l'op´┐Żration sendfile(2).

Sur la plupart des plateformes, l'utilisation de sendfile am´┐Żliore les performances en ´┐Żliminant les m´┐Żcanismes de lecture et envoi s´┐Żpar´┐Żs. Dans certains cas cependant, l'utilisation de sendfile peut nuire ´┐Ż la stabilit´┐Ż du d´┐Żmon httpd :

Pour les installations o´┐Ż une de ces situations peut se produire, vous devez utiliser EnableSendfile off afin de d´┐Żsactiver la mise ´┐Ż disposition de contenus de fichiers par sendfile. (Note : il est possible de passer outre cette directive au niveau de chaque r´┐Żpertoire.)

Process Creation

Avant Apache 1.3, les directives MinSpareServers, MaxSpareServers, et StartServers avaient des effets drastiques sur les performances de r´┐Żf´┐Żrence. En particulier, Apache avait besoin d'un d´┐Żlai de "mont´┐Że en puissance" afin d'atteindre un nombre de processus enfants suffisant pour supporter la charge qui lui ´┐Żtait appliqu´┐Że. Apr´┐Żs le lancement initial des processus enfants par StartServers, seulement un processus enfant par seconde ´┐Żtait cr´┐Ż´┐Ż afin d'atteindre la valeur de la directive MinSpareServers. Ainsi, un serveur acc´┐Żd´┐Ż par 100 clients simultan´┐Żs et utilisant la valeur par d´┐Żfaut de 5 pour la directive StartServers, n´┐Żcessitait environ 95 secondes pour lancer suffisamment de processus enfants permettant de faire face ´┐Ż la charge. Ceci fonctionne en pratique pour les serveurs en production, car ils sont rarement red´┐Żmarr´┐Żs. Ce n'est cependant pas le cas pour les tests de r´┐Żf´┐Żrence (benchmarks) o´┐Ż le serveur ne fonctionne que 10 minutes.

La r´┐Żgle "un processus par seconde" avait ´┐Żt´┐Ż impl´┐Żment´┐Że afin d'´┐Żviter l'enlisement de la machine dans le d´┐Żmarrage de nouveaux processus enfants. Pendant que la machine est occup´┐Że ´┐Ż lancer des processus enfants, elle ne peut pas traiter les requ´┐Żtes. Mais cette r´┐Żgle impactait tellement la perception des performances d'Apache qu'elle a d´┐Ż ´┐Żtre remplac´┐Że. A partir d'Apache 1.3, le code a assoupli la r´┐Żgle "un processus par seconde". Il va en lancer un, attendre une seconde, puis en lancer deux, attendre une seconde, puis en lancer quatre et ainsi de suite jusqu'´┐Ż lancer 32 processus. Il s'arr´┐Żtera lorsque le nombre de processus aura atteint la valeur d´┐Żfinie par la directive MinSpareServers.

Ceci s'av´┐Żre suffisamment r´┐Żactif pour pouvoir en g´┐Żn´┐Żral se passer de manipuler les valeurs des directives MinSpareServers, MaxSpareServers et StartServers. Lorsque plus de 4 processus enfants sont lanc´┐Żs par seconde, un message est ´┐Żmis vers le journal des erreurs. Si vous voyez appara´┐Żtre souvent ce genre de message, vous devez vous pencher sur ces r´┐Żglages. Pour vous guider, utilisez les informations d´┐Żlivr´┐Żes par le module mod_status.

´┐Ż mettre en relation avec la cr´┐Żation de processus, leur destruction est d´┐Żfinie par la valeur de la directive MaxConnectionsPerChild. Sa valeur par d´┐Żfaut est 0, ce qui signifie qu'il n'y a pas de limite au nombre de connexions qu'un processus enfant peut traiter. Si votre configuration actuelle a cette directive r´┐Żgl´┐Że ´┐Ż une valeur tr´┐Żs basse, de l'ordre de 30, il est conseill´┐Ż de l'augmenter de mani´┐Żre significative. Si vous utilisez SunOs ou une ancienne version de Solaris, utilisez une valeur de l'ordre de 10000 ´┐Ż cause des fuites de m´┐Żmoire.

Lorsqu'ils sont en mode "keep-alive", les processus enfants sont maintenus et ne font rien sinon attendre la prochaine requ´┐Żte sur la connexion d´┐Żj´┐Ż ouverte. La valeur par d´┐Żfaut de 5 de la directive KeepAliveTimeout tend ´┐Ż minimiser cet effet. Il faut trouver le bon compromis entre la bande passante r´┐Żseau et les ressources du serveur. En aucun cas vous ne devez choisir une valeur sup´┐Żrieure ´┐Ż 60 seconds, car la plupart des b´┐Żn´┐Żfices sont alors perdus.

top

Optimisation de la configuration ´┐Ż la compilation

Choisir un Module Multi-Processus (MPM)

Apache 2.x supporte les mod´┐Żles simultan´┐Żs enfichables, appel´┐Żs Modules Multi-Processus (MPMs). Vous devez choisir un MPM au moment de la construction d'Apache. Certaines plateformes ont des modules MPM sp´┐Żcifiques : mpm_netware, mpmt_os2 et mpm_winnt. Sur les syst´┐Żmes de type Unix, vous avez le choix entre un grand nombre de modules MPM. Le choix du MPM peut affecter la vitesse et l'´┐Żvolutivit´┐Ż du d´┐Żmon httpd :

Pour plus d'informations sur ces deux MPMs et les autres, veuillez vous r´┐Żf´┐Żrer ´┐Ż la documentation sur les MPM.

Modules

Comme le contr´┐Żle de l'utilisation de la m´┐Żmoire est tr´┐Żs important en mati´┐Żre de performance, il est conseill´┐Ż d'´┐Żliminer les modules que vous n'utilisez pas vraiment. Si vous avez construit ces modules en tant que DSOs, leur ´┐Żlimination consiste simplement ´┐Ż commenter la directive LoadModule associ´┐Że ´┐Ż ce module. Ceci vous permet de v´┐Żrifier si votre site fonctionne toujours apr´┐Żs la suppression de tel ou tel module.

Par contre, si les modules que vous voulez supprimer sont li´┐Żs statiquement ´┐Ż votre binaire Apache, vous devrez recompiler ce dernier afin de pouvoir les ´┐Żliminer.

La question qui d´┐Żcoule de ce qui pr´┐Żc´┐Żde est ´┐Żvidemment de savoir de quels modules vous avez besoin et desquels vous pouvez vous passer. La r´┐Żponse sera bien entendu diff´┐Żrente d'un site web ´┐Ż l'autre. Cependant, la liste minimale de modules n´┐Żcessaire ´┐Ż la survie de votre site contiendra certainement mod_mime, mod_dir et mod_log_config. mod_log_config est bien entendu optionnel puisque vous pouvez faire fonctionner un site web en se passant de fichiers journaux ; ceci est cependant d´┐Żconseill´┐Ż.

Op´┐Żrations atomiques

Certains modules, ´┐Ż l'instar de mod_cache et des versions de d´┐Żveloppement r´┐Żcentes du MPM worker, utilisent l'API atomique d'APR. Cette API propose des op´┐Żrations atomiques que l'on peut utiliser pour all´┐Żger la synchronisation des threads.

Par d´┐Żfaut, APR impl´┐Żmente ces op´┐Żrations en utilisant les m´┐Żcanismes les plus efficaces disponibles sur chaque plateforme cible (Syst´┐Żme d'exploitation et processeur). De nombreux processeurs modernes, par exemple, poss´┐Żdent une instruction qui effectue une op´┐Żration atomique de type comparaison et ´┐Żchange ou compare-and-swap (CAS) au niveau mat´┐Żriel. Sur certaines platesformes cependant, APR utilise par d´┐Żfaut une impl´┐Żmentation de l'API atomique plus lente, bas´┐Że sur les mutex, afin d'assurer la compatibilit´┐Ż avec les anciens mod´┐Żles de processeurs qui ne poss´┐Żdent pas ce genre d'instruction. Si vous construisez Apache pour une de ces platesformes, et ne pr´┐Żvoyez de l'ex´┐Żcuter que sur des processeurs r´┐Żcents, vous pouvez s´┐Żlectionner une impl´┐Żmentation atomique plus rapide ´┐Ż la compilation en utilisant l'option --enable-nonportable-atomics du script configure :

./buildconf
./configure --with-mpm=worker --enable-nonportable-atomics=yes

L'option --enable-nonportable-atomics concerne les platesformes suivantes :

Module mod_status et ExtendedStatus On

Si vous incluez le module mod_status ´┐Ż la construction d'Apache et ajoutez ExtendedStatus On ´┐Ż sa configuration, Apache va effectuer pour chaque requ´┐Żte deux appels ´┐Ż gettimeofday(2) (ou times(2) selon votre syst´┐Żme d'exploitation), et (pour les versions ant´┐Żrieures ´┐Ż 1.3) de nombreux appels suppl´┐Żmentaires ´┐Ż time(2). Tous ces appels sont effectu´┐Żs afin que le rapport de statut puisse contenir des indications temporelles. Pour am´┐Żliorer les performances, utilisez ExtendedStatus off (qui est le r´┐Żglage par d´┐Żfaut).

accept Serialization - points de connexion ´┐Ż un programme (sockets) multiples

Mise en garde :

Cette section n'a pas ´┐Żt´┐Ż totalement mise ´┐Ż jour car elle ne tient pas compte des changements intervenus dans la version 2.x du Serveur HTTP Apache. Certaines informations sont encore pertinentes, il vous est cependant conseill´┐Ż de les utiliser avec prudence.

Ce qui suit est une br´┐Żve discussion ´┐Ż propos de l'API des sockets Unix. Supposons que votre serveur web utilise plusieurs directives Listen afin d'´┐Żcouter plusieurs ports ou de multiples adresses. Afin de tester chaque socket pour voir s'il a une connexion en attente, Apache utilise select(2). select(2) indique si un socket a z´┐Żro ou au moins une connexion en attente. Le mod´┐Żle d'Apache comporte plusieurs processus enfants, et tous ceux qui sont inactifs testent la pr´┐Żsence de nouvelles connexions au m´┐Żme moment. Une impl´┐Żmentation rudimentaire de ceci pourrait ressembler ´┐Ż l'exemple suivant (ces exemples ne sont pas extraits du code d'Apache, ils ne sont propos´┐Żs qu'´┐Ż des fins p´┐Żdagogiques) :

        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);
        }

Mais cette impl´┐Żmentation rudimentaire pr´┐Żsente une s´┐Żrieuse lacune. Rappelez-vous que les processus enfants ex´┐Żcutent cette boucle au m´┐Żme moment ; ils vont ainsi bloquer sur select s'ils se trouvent entre deux requ´┐Żtes. Tous ces processus bloqu´┐Żs vont se r´┐Żactiver et sortir de select quand une requ´┐Żte va appara´┐Żtre sur un des sockets (le nombre de processus enfants qui se r´┐Żactivent varie en fonction du syst´┐Żme d'exploitation et des r´┐Żglages de synchronisation). Ils vont alors tous entrer dans la boucle et tenter un "accept" de la connexion. Mais seulement un d'entre eux y parviendra (en supposant qu'il ne reste q'une seule connexion en attente), les autres vont se bloquer au niveau de accept. Ceci verrouille vraiment ces processus de telle sorte qu'ils ne peuvent plus servir de requ´┐Żtes que par cet unique socket, et il en sera ainsi jusqu'´┐Ż ce que suffisamment de nouvelles requ´┐Żtes apparaissent sur ce socket pour les r´┐Żactiver tous. Cette lacune a ´┐Żt´┐Ż document´┐Że pour la premi´┐Żre fois dans PR#467. Il existe au moins deux solutions.

La premi´┐Żre consiste ´┐Ż rendre les sockets non blocants. Dans ce cas, accept ne bloquera pas les processus enfants, et ils pourront continuer ´┐Ż s'ex´┐Żcuter imm´┐Żdiatement. Mais ceci consomme des ressources processeur. Supposons que vous ayez dix processus enfants inactifs dans select, et qu'une connexion arrive. Neuf des dix processus vont se r´┐Żactiver, tenter un accept de la connexion, ´┐Żchouer, et boucler dans select, tout en n'ayant finalement rien accompli. Pendant ce temps, aucun de ces processus ne traite les requ´┐Żtes qui arrivent sur d'autres sockets jusqu'´┐Ż ce qu'ils retournent dans select. Finalement, cette solution ne semble pas tr´┐Żs efficace, ´┐Ż moins que vous ne disposiez d'autant de processeurs inactifs (dans un serveur multiprocesseur) que de processus enfants inactifs, ce qui n'est pas une situation tr´┐Żs courante.

Une autre solution, celle qu'utilise Apache, consiste ´┐Ż s´┐Żrialiser les entr´┐Żes dans la boucle interne. La boucle ressemble ´┐Ż ceci (les diff´┐Żrences sont mises en surbrillance) :

        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;
        }

Les fonctions accept_mutex_on et accept_mutex_off impl´┐Żmentent un s´┐Żmaphore permettant une exclusion mutuelle. Un seul processus enfant ´┐Ż la fois peut poss´┐Żder le mutex. Plusieurs choix se pr´┐Żsentent pour impl´┐Żmenter ces mutex. Ce choix est d´┐Żfini dans src/conf.h (versions ant´┐Żrieures ´┐Ż 1.3) ou src/include/ap_config.h (versions 1.3 ou sup´┐Żrieures). Certaines architectures ne font pas ce choix du mode de verrouillage ; l'utilisation de directives Listen multiples sur ces architectures est donc peu s´┐Żr.

La directive Mutex permet de modifier l'impl´┐Żmentation du mutex mpm-accept ´┐Ż l'ex´┐Żcution. Des consid´┐Żrations sp´┐Żcifiques aux diff´┐Żrentes impl´┐Żmentations de mutex sont document´┐Żes avec cette directive.

Une autre solution qui a ´┐Żt´┐Ż imagin´┐Że mais jamais impl´┐Żment´┐Że, consiste ´┐Ż s´┐Żrialiser partiellement la boucle -- c'est ´┐Ż dire y faire entrer un certain nombre de processus. Ceci ne pr´┐Żsenterait un int´┐Żr´┐Żt que sur les machines multiprocesseurs o´┐Ż plusieurs processus enfants peuvent s'ex´┐Żcuter simultan´┐Żment, et encore, la s´┐Żrialisation ne tire pas vraiment parti de toute la bande passante. C'est une possibilit´┐Ż d'investigation future, mais demeure de priorit´┐Ż basse car les serveurs web ´┐Ż architecture hautement parall´┐Żle ne sont pas la norme.

Pour bien faire, vous devriez faire fonctionner votre serveur sans directives Listen multiples si vous visez les performances les plus ´┐Żlev´┐Żes. Mais lisez ce qui suit.

accept Serialization - point de connexion ´┐Ż un programme (sockets) unique

Ce qui pr´┐Żc´┐Żde convient pour les serveurs ´┐Ż sockets multiples, mais qu'en est-il des serveurs ´┐Ż socket unique ? En th´┐Żorie, ils ne devraient pas rencontrer les m´┐Żmes probl´┐Żmes car tous les processus enfants peuvent se bloquer dans accept(2) jusqu'´┐Ż ce qu'une connexion arrive, et ils ne sont pas utilis´┐Żs ´┐Ż ne rien faire. En pratique, ceci dissimule un m´┐Żme comportement de bouclage discut´┐Ż plus haut dans la solution non-blocante. De la mani´┐Żre dont sont impl´┐Żment´┐Żes les piles TCP, le noyau r´┐Żactive v´┐Żritablement tous les processus bloqu´┐Żs dans accept quand une seule connexion arrive. Un de ces processus prend la connexion en compte et retourne dans l'espace utilisateur, les autres bouclant dans l'espace du noyau et se d´┐Żsactivant quand ils s'aper´┐Żoivent qu'il n'y a pas de connexion pour eux. Ce bouclage est invisible depuis le code de l'espace utilisateur, mais il est quand-m´┐Żme pr´┐Żsent. Ceci peut conduire ´┐Ż la m´┐Żme augmentation de charge ´┐Ż perte que la solution non blocante au cas des sockets multiples peut induire.

Pour cette raison, il appara´┐Żt que de nombreuses architectures se comportent plus "proprement" si on s´┐Żrialise m´┐Żme dans le cas d'une socket unique. Il s'agit en fait du comportement par d´┐Żfaut dans la plupart des cas. Des exp´┐Żriences pouss´┐Żes sous Linux (noyau 2.0.30 sur un biprocesseur Pentium pro 166 avec 128 Mo de RAM) ont montr´┐Ż que la s´┐Żrialisation d'une socket unique provoque une diminution inf´┐Żrieure ´┐Ż 3% du nombre de requ´┐Żtes par secondes par rapport au traitement non s´┐Żrialis´┐Ż. Mais le traitement non s´┐Żrialis´┐Ż des sockets uniques induit un temps de r´┐Żponse suppl´┐Żmentaire de 100 ms pour chaque requ´┐Żte. Ce temps de r´┐Żponse est probablement provoqu´┐Ż par une limitation sur les lignes ´┐Ż haute charge, et ne constitue un probl´┐Żme que sur les r´┐Żseaux locaux. Si vous voulez vous passer de la s´┐Żrialisation des sockets uniques, vous pouvez d´┐Żfinir SINGLE_LISTEN_UNSERIALIZED_ACCEPT et les serveurs ´┐Ż socket unique ne pratiqueront plus du tout la s´┐Żrialisation.

Fermeture en prenant son temps (Lingering close)

Comme discut´┐Ż dans draft-ietf-http-connection-00.txt section 8, pour impl´┐Żmenter de mani´┐Żre fiable le protocole, un serveur HTTP doit fermer les deux directions d'une communication ind´┐Żpendamment (rappelez-vous qu'une connexion TCP est bidirectionnelle, chaque direction ´┐Żtant ind´┐Żpendante de l'autre).

Quand cette fonctionnalit´┐Ż fut ajout´┐Że ´┐Ż Apache, elle causa une avalanche de probl´┐Żmes sur plusieurs versions d'Unix ´┐Ż cause d'une impl´┐Żmentation ´┐Ż courte vue. La sp´┐Żcification TCP ne pr´┐Żcise pas que l'´┐Żtat FIN_WAIT_2 poss´┐Żde un temps de r´┐Żponse mais elle ne l'exclut pas. Sur les syst´┐Żmes qui n'introduisent pas ce temps de r´┐Żponse, Apache 1.2 induit de nombreux blocages d´┐Żfinitifs de socket dans l'´┐Żtat FIN_WAIT_2. On peut eviter ceci dans de nombreux cas tout simplement en mettant ´┐Ż jour TCP/IP avec le dernier patch mis ´┐Ż disposition par le fournisseur. Dans les cas o´┐Ż le fournisseur n'a jamais fourni de patch (par exemple, SunOS4 -- bien que les utilisateurs poss´┐Żdant une license source puissent le patcher eux-m´┐Żmes), nous avons d´┐Żcid´┐Ż de d´┐Żsactiver cette fonctionnalit´┐Ż.

Il y a deux m´┐Żthodes pour arriver ´┐Ż ce r´┐Żsultat. La premi´┐Żre est l'option de socket SO_LINGER. Mais le sort a voulu que cette solution ne soit jamais impl´┐Żment´┐Że correctement dans la plupart des piles TCP/IP. Et m´┐Żme dans les rares cas o´┐Ż cette solution a ´┐Żt´┐Ż impl´┐Żment´┐Że correctement (par exemple Linux 2.0.31), elle se montre beaucoup plus gourmande (en temps processeur) que la solution suivante.

Pour la plus grande partie, Apache impl´┐Żmente cette solution ´┐Ż l'aide d'une fonction appel´┐Że lingering_close (d´┐Żfinie dans http_main.c). La fonction ressemble approximativement ´┐Ż ceci :

        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);
        }

Ceci ajoute naturellement un peu de charge ´┐Ż la fin d'une connexion, mais s'av´┐Żre n´┐Żcessaire pour une impl´┐Żmentation fiable. Comme HTTP/1.1 est de plus en plus pr´┐Żsent et que toutes les connexions sont persistentes, la charge sera amortie par la multiplicit´┐Ż des requ´┐Żtes. Si vous voulez jouer avec le feu en d´┐Żsactivant cette fonctionnalit´┐Ż, vous pouvez d´┐Żfinir NO_LINGCLOSE, mais c'est fortement d´┐Żconseill´┐Ż. En particulier, comme les connexions persistantes en pipeline de HTTP/1.1 commencent ´┐Ż ´┐Żtre utilis´┐Żes, lingering_close devient une absolue n´┐Żcessit´┐Ż (et les connexions en pipeline sont plus rapides ; vous avez donc tout int´┐Żr´┐Żt ´┐Ż les supporter).

Fichier tableau de bord (Scoreboard file)

Les processus parent et enfants d'Apache communiquent entre eux ´┐Ż l'aide d'un objet appel´┐Ż "Tableau de bord" (Scoreboard). Id´┐Żalement, cet ´┐Żchange devrait s'effectuer en m´┐Żmoire partag´┐Że. Pour les syst´┐Żmes d'exploitation auxquels nous avons eu acc´┐Żs, ou pour lesquels nous avons obtenu des informations suffisamment d´┐Żtaill´┐Żes pour effectuer un portage, cet ´┐Żchange est en g´┐Żn´┐Żral impl´┐Żment´┐Ż en utilisant la m´┐Żmoire partag´┐Że. Pour les autres, on utilise par d´┐Żfaut un fichier d'´┐Żchange sur disque. Le fichier d'´┐Żchange sur disque est non seulement lent, mais aussi peu fiable (et propose moins de fonctionnalit´┐Żs). Recherchez dans le fichier src/main/conf.h correspondant ´┐Ż votre architecture soit USE_MMAP_SCOREBOARD, soit USE_SHMGET_SCOREBOARD. La d´┐Żfinition de l'un des deux (ainsi que leurs compagnons respectifs HAVE_MMAP et HAVE_SHMGET), active le code fourni pour la m´┐Żmoire partag´┐Że. Si votre syst´┐Żme propose une autre solution pour la gestion de la m´┐Żmoire partag´┐Że, ´┐Żditez le fichier src/main/http_main.c et ajoutez la portion de code n´┐Żcessaire pour pouvoir l'utiliser dans Apache (Merci de nous envoyer aussi le patch correspondant).

Note ´┐Ż caract´┐Żre historique : le portage d'Apache sous Linux n'utilisait pas la m´┐Żmoire partag´┐Że avant la version 1.2. Ceci entra´┐Żnait un comportement tr´┐Żs rudimentaire et peu fiable des versions ant´┐Żrieures d'Apache sous Linux.

DYNAMIC_MODULE_LIMIT

Si vous n'avez pas l'intention d'utiliser les modules charg´┐Żs dynamiquement (ce qui est probablement le cas si vous ´┐Żtes en train de lire ce document afin de personnaliser votre serveur en recherchant le moindre des gains en performances), vous pouvez ajouter la d´┐Żfinition -DDYNAMIC_MODULE_LIMIT=0 ´┐Ż la construction de votre serveur. Ceci aura pour effet de lib´┐Żrer la m´┐Żmoire RAM allou´┐Że pour le chargement dynamique des modules.

top

Appendice : Analyse d´┐Żtaill´┐Że d'une trace

Voici la trace d'un appel syst´┐Żme d'Apache 2.0.38 avec le MPM worker sous Solaris 8. Cette trace a ´┐Żt´┐Ż collect´┐Że ´┐Ż l'aide de la commande :

truss -l -p httpd_child_pid.

L'option -l demande ´┐Ż truss de tracer l'ID du LWP (lightweight process--la version de Solaris des threads niveau noyau) qui invoque chaque appel syst´┐Żme.

Les autres syst´┐Żmes peuvent proposer des utilitaires de tra´┐Żage des appels syst´┐Żme diff´┐Żrents comme strace, ktrace, ou par. Ils produisent cependant tous une trace similaire.

Dans cette trace, un client a demand´┐Ż un fichier statique de 10 ko au d´┐Żmon httpd. Le tra´┐Żage des requ´┐Żtes pour des contenus non statiques ou comportant une n´┐Żgociation de contenu a une pr´┐Żsentation diff´┐Żrente (et m´┐Żme assez laide dans certains cas).

/67:    accept(3, 0x00200BEC, 0x00200C0C, 1) (sleeping...)
/67:    accept(3, 0x00200BEC, 0x00200C0C, 1)            = 9

Dans cette trace, le thread ´┐Ż l'´┐Żcoute s'ex´┐Żcute ´┐Ż l'int´┐Żrieur de LWP #67.

Notez l'absence de la s´┐Żrialisation d'accept(2). Sur cette plateforme sp´┐Żcifique, le MPM worker utilise un accept non s´┐Żrialis´┐Ż par d´┐Żfaut sauf s'il est en ´┐Żcoute sur des ports multiples.
/65:    lwp_park(0x00000000, 0)                         = 0
/67:    lwp_unpark(65, 1)                               = 0

Apr´┐Żs avoir accept´┐Ż la connexion, le thread ´┐Ż l'´┐Żcoute r´┐Żactive un thread du worker pour effectuer le traitement de la requ´┐Żte. Dans cette trace, le thread du worker qui traite la requ´┐Żte est associ´┐Ż ´┐Ż LWP #65.

/65:    getsockname(9, 0x00200BA4, 0x00200BC4, 1)       = 0

Afin de pouvoir impl´┐Żmenter les h´┐Żtes virtuels, Apache doit conna´┐Żtre l'adresse du socket local utilis´┐Ż pour accepter la connexion. On pourrait supprimer cet appel dans de nombreuses situations (par exemple dans le cas o´┐Ż il n'y a pas d'h´┐Żte virtuel ou dans le cas o´┐Ż les directives Listen contiennent des adresses sans caract´┐Żres de substitution). Mais aucun effort n'a ´┐Żt´┐Ż accompli ´┐Ż ce jour pour effectuer ces optimisations.

/65:    brk(0x002170E8)                                 = 0
/65:    brk(0x002190E8)                                 = 0

L'appel brk(2) alloue de la m´┐Żmoire dans le tas. Ceci est rarement visible dans une trace d'appel syst´┐Żme, car le d´┐Żmon httpd utilise des allocateurs m´┐Żmoire de son cru (apr_pool et apr_bucket_alloc) pour la plupart des traitements de requ´┐Żtes. Dans cette trace, le d´┐Żmon httpd vient juste de d´┐Żmarrer, et il doit appeler malloc(3) pour r´┐Żserver les blocs de m´┐Żmoire n´┐Żcessaires ´┐Ż la cr´┐Żation de ses propres allocateurs de m´┐Żmoire.

/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)                   = 0

Ensuite, le thread de worker passe la connexion du client (descripteur de fichier 9) en mode non blocant. Les appels setsockopt(2) et getsockopt(2) constituent un effet de bord de la mani´┐Żre dont la libc de Solaris utilise fcntl(2) pour les sockets.

/65:    read(9, " G E T   / 1 0 k . h t m".., 8000)     = 97

Le thread de worker lit la requ´┐Żte du client.

/65:    stat("/var/httpd/apache/httpd-8999/htdocs/10k.html", 0xFAF7B978) = 0
/65:    open("/var/httpd/apache/httpd-8999/htdocs/10k.html", O_RDONLY) = 10

Ce d´┐Żmon httpd a ´┐Żt´┐Ż configur´┐Ż avec les options Options FollowSymLinks et AllowOverride None. Il n'a donc ni besoin d'appeler lstat(2) pour chaque r´┐Żpertoire du chemin du fichier demand´┐Ż, ni besoin de v´┐Żrifier la pr´┐Żsence de fichiers .htaccess. Il appelle simplement stat(2) pour v´┐Żrifier d'une part que le fichier existe, et d'autre part que c'est un fichier r´┐Żgulier, et non un r´┐Żpertoire.

/65:    sendfilev(0, 9, 0x00200F90, 2, 0xFAF7B53C)      = 10269

Dans cet exemple, le d´┐Żmon httpd peut envoyer l'en-t´┐Żte de la r´┐Żponse HTTP et le fichier demand´┐Ż ´┐Ż l'aide d'un seul appel syst´┐Żme sendfilev(2). La s´┐Żmantique de sendfile varie en fonction des syst´┐Żmes d'exploitation. Sur certains autres syst´┐Żmes, il faut faire un appel ´┐Ż write(2) ou writev(2) pour envoyer les en-t´┐Żtes avant d'appeler sendfile(2).

/65:    write(4, " 1 2 7 . 0 . 0 . 1   -  ".., 78)      = 78

Cet appel ´┐Ż write(2) enregistre la requ´┐Żte dans le journal des acc´┐Żs. Notez qu'une des choses manquant ´┐Ż cette trace est un appel ´┐Ż time(2). A la diff´┐Żrence d'Apache 1.3, Apache 2.x utilise gettimeofday(3) pour consulter l'heure. Sur certains syst´┐Żmes d'exploitation, comme Linux ou Solaris, gettimeofday est impl´┐Żment´┐Ż de mani´┐Żre optimis´┐Że de telle sorte qu'il consomme moins de ressources qu'un appel syst´┐Żme habituel.

/65:    shutdown(9, 1, 1)                               = 0
/65:    poll(0xFAF7B980, 1, 2000)                       = 1
/65:    read(9, 0xFAF7BC20, 512)                        = 0
/65:    close(9)                                        = 0

Le thread de worker effectue une fermeture "en prenant son temps" (lingering close) de la connexion.

/65:    close(10)                                       = 0
/65:    lwp_park(0x00000000, 0)         (sleeping...)

Enfin, le thread de worker ferme le fichier qu'il vient de d´┐Żlivrer et se bloque jusqu'´┐Ż ce que le thread en ´┐Żcoute lui assigne une autre connexion.

/67:    accept(3, 0x001FEB74, 0x001FEB94, 1) (sleeping...)

Pendant ce temps, le thread ´┐Ż l'´┐Żcoute peut accepter une autre connexion ´┐Ż partir du moment o´┐Ż il a assign´┐Ż la connexion pr´┐Żsente ´┐Ż un thread de worker (selon une certaine logique de contr´┐Żle de flux dans le MPM worker qui impose des limites au thread ´┐Ż l'´┐Żcoute si tous les threads de worker sont occup´┐Żs). Bien que cela n'apparaisse pas dans cette trace, l'accept(2) suivant peut (et le fait en g´┐Żn´┐Żral, en situation de charge ´┐Żlev´┐Że) s'ex´┐Żcuter en parall´┐Żle avec le traitement de la connexion qui vient d'´┐Żtre accept´┐Że par le thread de worker.

Langues Disponibles:  en  |  fr  |  ko  |  tr 

top

Commentaires

Notice:
This is not a Q&A section. Comments placed here should be pointed towards suggestions on improving the documentation or server, and may be removed again by our moderators if they are either implemented or considered invalid/off-topic. Questions on how to manage the Apache HTTP Server should be directed at either our IRC channel, #httpd, on Freenode, or sent to our mailing lists.