Versión 2.5 del Servidor HTTP Apache
Descripción: | Una variante del MPM worker con el
objetivo de consumir hilos sólo para conexiones con procesamiento
activo |
---|---|
Estado: | MPM |
Identificador de Módulos: | mpm_event_module |
Fichero de Código Fuente: | event.c |
El Módulo de Multi-Proceso (MPM en inglés) event
es,
como su nombre indica, una implementación asíncrona basada en eventos,
diseñada para permitir que se sirvan más peticiones simultáneamente
mediante la concesión de algo de trabajo de procesamiento a los hilos
"listeners" (de escucha), liberando a los hilos worker (trabajadores)
para servir más peticiones.
Para usar el MPM event
, añada
--with-mpm=event
a los parámetros del script
configure
cuando esté compilando
httpd
.
event
está basado en el MPM worker
, que implmementa un servidor híbrido de multi-proceso multi-hilo. Un solo proceso (el padre) es responsable de lanzar procesos child (hijos). Cada proceso child crea un número fijo de hilos de servidor tal y como se especifica en la directiva
ThreadsPerChild
, así como un hilo listener que está en escucha para recibir conexiones y las pasa al hilo worker para procesamiento según van llegando.
Las directivas de configuración en tiempo real son idénticas a aquellas facilitadas por worker
, con la única diferencia de que event además tiene la directiva
AsyncRequestWorkerFactor
.
El objetivo original de este MPM era arreglar el 'problema del keep alive' en HTTP. Después de que un cliente completa su primera petición, puede mantener la conexión abierta, enviando más peticiones utilizando el mismo socket y ahorrando una cantidad significativa de tiempo en abrir nuevas conexiones TCP. Sin embargo, el Servidor Apache HTTP tradicionalmente mantiene un proceso/hilo child esperando a que le lleguen datos del cliente, lo cual tiene sus propias desventajas. Para resolver este problema, este MPM usa un hilo dedicado de tipo listener en cada proceso junto con un grupo de hilos worker, compartiendo colas específicas para esas peticiones en modo keep-alive (o, más sencillamente, "readable"), aquellos en modo terminando-escritura, y aquellos en proceso de cerrarse ("closing"). Un bucle de eventos, activado por el estado de disponibilidad del socket, ajusta estas colas y manda el trabajo al grupo de workers.
Esta nueva arquitectura, haciendo uso de sockets no bloqueantes y características de kernel modernos expuestos por APR (como epoll de Linux), ya no necesita mpm-accept
Mutex
configurado para evitar el problema de thundering herd (manada estruendosa).
La cantidad total de conexiones que un solo bloque de proceso/hilo puede gestionar se regula con la directiva
AsyncRequestWorkerFactor
.
Las conexiones Async necesitarían un hilo worker fijo dedicado con los MPMs previos, pero no con event. La página de estado de
mod_status
muestra columnas nuevas bajo la sección de conexiones Async:
write()
al socket devuelve EWOULDBLOCK
o EAGAIN
, para volver a estar disponible para escritura de nuevo después de un tiempo de inactividad. El worker que tiene en uso el socket podría ser capaz de liberar la tarea de espera al hilo listener, que a cambio la reasinará al primer hilo worker inactivo disponible una cuando se eleve un evento para el socket (por ejemplo, "el socket está ahora disponible para escritura"). Por favor compruebe la sección de Limitaciones para más información.
KeepAliveTimeout
entonces el el listener cerrará el socket. En este método los hilos worker no son responsables de los socket inactivos y pueden reutilizarse para atender otras peticiones.Estas mejoras son válidas para ambas conexiones HTTP/HTTPS.
Los estados de conexión indicados más arriba se gestionan por el hilo listener a través de colas dedicadas, que hasta la versión 2.4.27 se comprobaban cada 100ms para encontrar llegaban a configuración de timeout como
Timeout
y
KeepAliveTimeout
. Esto era una solución sencilla y eficiente, pero presentaba un problema, el pollset forzaba un wake-up del hilo listener incluso si no había necesidad (por ejemplo aunque estuviera completamente inactivo), malgastando recursos. A partir de la versión 2.4.28 estas colas se gestionarán completamente a través de la lógica basada en eventos, no dependiendo ya de un polling activo. Los entornos con pocos recursos, como servidores embebidos, se beneficiarán de esta mejora.
Este mpm mostró algunos cuellos de botella de escalabilidad en el pasado llevando al siguiente error: "scoreboard is full, not at MaxRequestWorkers".
MaxRequestWorkers
limita el número de peticiones simultáneas que van a ser atendidas en un momento dado y también el número de procesos permitidos
(MaxRequestWorkers
/ ThreadsPerChild
), mientras tanto el Scoreboard es una representación de todos los procesos que se están ejecutando y el estado de sus hilos worker. Si el scoreboard está lleno (de manera que todos los hilos tienen un estado que no es inactivo) pero el número de peticiones activas servidas no es
MaxRequestWorkers
, significa que algunos de ellos están bloqueando nuevas peticiones que podrían servirse pero que se están encolando en su lugar (hasta el límite impuesto por
ListenBacklog
). La mayor parte de las veces los hilos están atascados en estado Graceful, concretamente están esperando a finalizar su trabajo con una conexión TCP para cerrar y liberar limpiamente un hueco en el scoreboard (por ejemplo gestionando peticiones que duran mucho, clientes lentos con conexiones con keep-alive activado). Dos escenarios son muy
comunes:
MaxSpareThreads
), esto es particularmente problemático porque cuando la carga se incrementa de nuevo, httpd intentará arrancar nuevos procesos. Si el patrón se repite, el número de procesos puede incrementarse bastante, y se puede acabar con una mezcla de procesos antiguos intentando parar y nuevos intentando hacer algún trabajo.
Desde la versión 2.4.24 en adelante, mpm-event es más inteligente y es capaz de gestionar los reinicios graceful mucho mejor. Algunas de las mejoras que trae son:
ServerLimit
.
MaxRequestWorkers
y
ThreadsPerChild
se usa para limitar la cantidad de procesos activos, mientras tanto
ServerLimit
también tiene en cuenta los que están haciendo un cierre graceful para permitir slots adicionales cuando sea necesario. La idea es usar
ServerLimit
para informar a httpd sobre cuántos procesos en total se toleran antes de impactar los recursos del sistema.
El comportamiento descrito en el último punto se puede observar completamente a través de mod_status
en la tabla de resumen de conexiones en dos nuevas columnas: "Slot" y "Stopping". La primera indica el PID y la última si el proceso está parando o no; el estado extra "Yes (old gen)" indica un proceso que todavía se está ejecutando después de un reinicio graceful.
La gestión de conexión mejorada podría no funcionar para ciertos filtros de conexión que se han declarado incompatibles con event. En estos casos, este MPM retornará al comportamiento del MPM
worker
y reservará un hilo worker por conexión. Todos los módulos incluidos con el servidor son compatibles con el MPM event.
Una restricción similar está actualmente presente para peticiones involucradas en un filtro de salida que necesita leer y/o modificar el cuerpo completo de la respuesta. Si la conexión al cliente se bloquea mientras el filtro está procesando los datos, y la cantidad de datos producidos por el filtro es demasiado grande para meterse en buffer de memoria, el hilo usado para esta petición no se libera mientras httpd espera hasta que los datos pendientes se envían al cliente.
Para ilustrar este punto podemos sopesar las dos situaciones siguientes:
servir un elemento estático (como por ejemplo un fichero CSS) en contraposición con servir contenido extraido de un servidor FCGI/CGI o un servidor al que se accede con servidor proxy. El primero es predecible, a saber, el MPM event tiene completa visibilidad en el final del contenido y puede usar eventos: el hilo worker sirviendo la respuesta puede hacer un desalojo de los primeros bytes hasta que se devuelve EWOULDBLOCK
o EAGAIN
, delegando el resto al listener. Este a cambio espera a un evento en el socket, y delega el trabajo para hacer una desalojo del resto del contenido al primero hilo worker inactivo. Mientras tanto, en el último ejemplo (FCGI/CGI/proxied content) el MPM no puede predecir el final de la respuesta y un hilo worker tiene que terminar su trabajo antes de devolver el control al listener. La única alternativa es almacenar la respuesta en un buffer de memoria, pero no sería la opción más segura en pos de la estabilidad del servidor y uso de memoria.
El modelo event fue posible por la introducción de APIs en los sistemas operativos soportados:
Antes de que estas APIs nuevas estuvieran disponibles, se tenían que usar las APIs tradicionales select
y
poll
.
Esas APIs se volvían lentas si se usaban para gestionar muchas conexiones o la posibilidad de un grupo de muchas conexiones repentinas era alta.
Las nuevas APIs permiten controlar muchas más conexiones y trabajan mucho mejor cuando el grupo de conexiones a controlar cambia frecuentemente. Así que estas APIs hicieron posible que se desarrollara el MPM event, que escala mucho mejor con el patrón típico HTTP de muchas conexiones inactivas.
El MPM asume que la implementación subyacente de apr_pollset
es razonablemente segura trabajando con hilos (threadsafe). Esto permite que el MPM evite un alto nivel de bloqueos, o tener que despertar el hilo listener para enviarle un socket keep-alive. Esto actualmente es sólo compatible con KQueue and EPoll.
Este MPM depende de operaciones atómicas de comparar-y-cambiar de APR para sincronización de hilos. Si está compilando para una máquina x86 y no necesita soportar 386, o está compilando para SPARC y no necesita funcionar en chips pre-UltraSPARC, añada
--enable-nonportable-atomics=yes
a los parámetros del script
configure
. Esto hará que APR implemente operaciones atómicas usando los opcode eficientes no disponibles en CPU's más antiguas.
Este MPM no rinde bien en plataformas más antiguas que no tienen un buen sistema multihilo, pero el requerimiento de EPoll o KQueue hace esto irrelevante.
libkse
(vea man libmap.conf
).glibc
ha sido compilada con soporte para EPoll.Descripción: | Limita el número de conexiones concurrentes por proceso |
---|---|
Sintaxis: | AsyncRequestWorkerFactor factor |
Valor por defecto: | 2 |
Contexto: | server config |
Estado: | MPM |
Módulo: | event |
Compatibilidad: | Disponible en versión 2.3.13 y posterior |
El MPM event gestiona algunas conexiónes de manera asíncrona, donde hilos worker de petición están solo alojados por cortos periodos de tiempos según es necesario, y otras conexiones con un hilo worker de petición reservado por conexión. Esto puede llevar a situaciones donde todos los workers están trabajando y no hay ningun hilo worker disponible para gestionar nuevo trabajo en las conexiones asíncronas establecidas.
Para mitigar este problema, el MPM event hace dos cosas:
Esta directiva puede usarse para afinar el límite de conexiones por-proceso. Un proceso solo aceptará conexiones nuevas si el número actual de conexiones (sin contar las que están en estado "closing") es menor que:
ThreadsPerChild
+
(AsyncRequestWorkerFactor
*
número de workers inactivos)
Una estimación del máximo de conexiones concurrentes entre todos los procesos dado un valor medio de hilos worker inactivos puede ser calculado con:
(ThreadsPerChild
+
(AsyncRequestWorkerFactor
*
número de workers inactivos)) *
ServerLimit
ThreadsPerChild = 10 ServerLimit = 4 AsyncRequestWorkerFactor = 2 MaxRequestWorkers = 40 workers_inactivos = 4 (media de todos los procesos para mantenerlo sencillo) max_conexiones = (ThreadsPerChild + (AsyncRequestWorkerFactor * idle_workers)) * ServerLimit = (10 + (2 * 4)) * 4 = 72
Cuando todos los hilos worker están inactivos, entonces el máximo absoluto de conexiones concurrentes puede calcularse de una forma más sencilla::
(AsyncRequestWorkerFactor
+ 1) *
MaxRequestWorkers
ThreadsPerChild = 10 ServerLimit = 4 MaxRequestWorkers = 40 AsyncRequestWorkerFactor = 2
Si todoso los procesos tienen hilos inactivos entonces:
idle_workers = 10
Podemos calcular el máximo absoluto de conexiones concurrentes de dos formas:
max_connections = (ThreadsPerChild + (AsyncRequestWorkerFactor * idle_workers)) * ServerLimit = (10 + (2 * 10)) * 4 = 120 max_connections = (AsyncRequestWorkerFactor + 1) * MaxRequestWorkers = (2 + 1) * 40 = 120
Configurar AsyncRequestWorkerFactor
requiere conocimiento sobre el tráfico que se recibe por httpd y cada caso de uso específico, así que cambiar el valor por defecto requiere comprobaciones y extracción de datos intensivas desde mod_status
.
MaxRequestWorkers
se llamaba
MaxClients
antes de la versión 2.3.13. El valor de más arriba muestra que el nombre antiguo no describía de una manera certera su significado para el MPM event.
AsyncRequestWorkerFactor
puede tomar valores numéricos no integrales, p. ej. "1.5".