Le module multi-processus (MPM)
Pour utiliser le MPM --with-mpm=event
aux arguments du script
Le MPM
Les directives de configuration à l'exécution sont identiques à celles que
propose le MPM
Ce module MPM a été conçu à l'origine pour résoudre le "problème keep alive" de HTTP. Lorsqu'un client a effectué une première requête, il peut garder la connexion ouverte et envoyer les requêtes suivante en utilisant le même socket, ce qui diminue considérablement la charge qui aurait été induite par la création de nouvelles connexions TCP. Cependant, le fonctionnement du serveur HTTP Apache impose de réserver un couple processus enfant/thread pour attendre les données en provenance du client, ce qui présente certains inconvénients. Pour résoudre ce problème, le MPM Event utilise un thread d'écoute dédié pour chaque processus associé à un jeu de threads de travail, partageant les files d'attentes spécifiques aux requêtes en mode keep-alive (ou plus simplement en mode "lisible"), à celles en mode écriture des résultats, et à celles en court de fermeture ("closing"). Une boucle d'attente d'évènements déclenchée en fonction du statut de la disponibilité du socket ajuste ces files d'attente et distribue le travail au jeu de threads de travail.
Cette nouvelle architecture, en exploitant les sockets non blocants et
les fonctionnalités des noyaux modernes mis en valeur par
mpm-accept
pour
éviter le problème de "thundering herd".
La directive
Avec les MPM précédents, les connexions asynchrones nécessitaient
un thread de travail dédié, mais ce n'est plus le cas avec le MPM Event.
La page d'état de
write()
vers le socket
renvoie en général EWOULDBLOCK
ou EAGAIN
pour que l'on puisse y écrire à nouveau après un certain temps
d'inactivité. Le thread de travail qui utilise le socket doit alors
être en mesure de récupérer la tâche en attente et la restituer au
thread d'écoute qui, à son tour, la réattribuera au premier thread
de travail disponible, lorsqu'un évènement sera généré pour le socket
(par exemple, "il est maintenant possible d'écrire dans le socket").
Veuillez vous reporter à la section à propos des limitations pour
plus de détails.
Ces améliorations sont disponible pour les connexions HTTP ou HTTPS.
La gestion améliorée des connexions peut ne pas fonctionner pour
certains filtres de connexion qui se sont déclarés eux-mêmes
incompatibles avec le MPM Event. Dans ce cas, le MPM Event réadoptera le
comportement du MPM
Une restriction similaire apparaît lorsqu'une requête utilise un
filtre en sortie qui doit pouvoir lire et/ou modifier la totalité du
corps de la réponse. Si la connexion avec le client se bloque pendant
que le filtre traite les données, et si la quantité de données produites
par le filtre est trop importante pour être stockée en mémoire, le
thread utilisé pour la requête n'est pas libéré pendant que httpd attend
que les données soient transmises au client.
Pour illustrer ce cas de figure, nous pouvons envisager les deux
situations suivantes : servir une ressource statique (comme un fichier
CSS) ou servir un contenu issu d'un programme FCGI/CGI ou d'un serveur
mandaté. La première situation est prévisible ; en effet, le MPM Event a
une parfaite visibilité sur la fin du contenu, et il peut utiliser les
évènements : le thread de travail qui sert la réponse peut envoyer les
premiers octets jusqu'à ce que EWOULDBLOCK
ou
EAGAIN
soit renvoyé, et déléguer le reste de la réponse au thread
d'écoute. Ce dernier en retour attend un évènement sur le socket, et
délègue le reste de la réponse au premier
thread de travail disponible. Dans la deuxième situation par contre
(FCGI/CGI/contenu mandaté), le MPM n'a pas de visibilité sur la fin de
la réponse, et le thread de travail doit terminer sa tâche avant de
rendre le contrôle au thread d'écoute. La seule solution consisterait
alors à stocker la réponse en mémoire, mais ce ne serait pas l'option la
plus sure en matière de stabilité du serveur et d'empreinte mémoire.
Le modèle event a été rendu possible par l'introduction de nouvelles APIs dans les systèmes d'exploitation supportés :
Avant que ces APIs soient mises à disposition, les APIs
traditionnelles select
et poll
devaient être
utilisées. Ces APIs deviennent lentes si on les utilise pour gérer de
nombreuses connexions ou si le jeu de connexions possède un taux de
renouvellement élevé. Les nouvelles APIs permettent de gérer beaucoup
plus de connexions et leur performances sont meilleures lorsque le jeu
de connexions à gérer change fréquemment. Ces APIs ont donc rendu
possible l'écriture le MPM Event qui est mieux adapté à la situation
HTTP typique où de nombreuses connexions sont inactives.
Le MPM Event suppose que l'implémentation de apr_pollset
sous-jacente est raisonnablement sure avec l'utilisation des threads
(threadsafe). Ceci évite au MPM de devoir effectuer trop verrouillages
de haut niveau, ou d'avoir à réveiller le thread d'écoute pour lui
envoyer un socket keep-alive. Ceci n'est possible qu'avec KQueue et
EPoll.
Ce MPM dépend des opérations atomiques compare-and-swap
d'--enable-nonportable-atomics=yes
aux arguments du
script
Ce MPM ne fonctionne pas de manière optimale sur les plates-formes plus anciennes qui ne gèrent pas correctement les threads, mais ce problème est sans objet du fait du prérequis concernant EPoll ou KQueue.
libkse
(voir man libmap.conf
).glibc
a été compilée
avec le support pour EPoll.Le MPM event gère certaines connexions de manière asynchrone ; dans ce cas, les threads traitant la requête sont alloués selon les besoins et pour de courtes périodes. Dans les autres cas, un thread est réservé par connexion. Ceci peut conduire à des situations où tous les threads sont saturés et où aucun thread n'est capable d'effectuer de nouvelles tâches pour les connexions asynchrones établies.
Pour minimiser les effets de ce problème, le MPM event utilise deux méthodes :
Cette directive permet de personnaliser finement la limite du nombre de connexions par thread. Un processus n'acceptera de nouvelles connexions que si le nombre actuel de connexions (sans compter les connexions à l'état "closing") est inférieur à :
Il est possible d'effectuer une estimation du nombre maximum de connexions simultanées pour tous les processus et pour un nombre donné moyen de threads de travail inactifs comme suit :
(
Lorsque tous les threads de travail sont inactifs, le nombre maximum absolu de connexions simultanées peut être calculé de manière plus simple :
(
Si tous les threads de tous les processus sont inactifs, alors :
Nous pouvons calculer le nombre maximum absolu de connexions simultanées de deux manières :
Le réglage de la directive
La directive
La directive