Estoy ejecutando un sitio de poco tráfico que experimenta un gran aumento de visitantes una vez por semana después de una actualización del sitio. Durante este pico, el rendimiento del sitio es extremadamente pobre en comparación con el resto de la semana. La carga real en los servidores sigue siendo muy baja, confiablemente por debajo del 10% de CPU y menos del 30% de RAM (el hardware debe ser excesivo para lo que realmente estamos haciendo), pero por alguna razón Apache parece ser incapaz de hacer frente a la cantidad de solicitudes. Estamos ejecutando apache 2.2.3 en RHEL 5.7, kernel 2.6.18-274.7.1.el5, x86_64.
Intentando reproducir este comportamiento durante las horas libres con ab, encuentro una caída importante en el rendimiento cuando supero aproximadamente 256 usuarios. Ejecutando la prueba con el caso de uso más pequeño posible que se me ocurrió (el archivo de texto estático se está recuperando, 223 bytes en total) el rendimiento es consistentemente normal con 245 solicitudes simultáneas:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 15 25 5.8 24 37
Processing: 15 65 22.9 76 96
Waiting: 15 64 23.0 76 96
Total: 30 90 27.4 100 125
Percentage of the requests served within a certain time (ms)
50% 100
66% 108
75% 111
80% 113
90% 118
95% 120
98% 122
99% 123
100% 125 (longest request)
Pero tan pronto como hago trinquete hasta 265 solicitudes simultáneas, un subconjunto de ellas comienza a tomar una cantidad absurda de tiempo para completar:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 13 195 692.6 26 3028
Processing: 15 65 21.3 72 100
Waiting: 15 65 21.3 71 99
Total: 32 260 681.7 101 3058
Percentage of the requests served within a certain time (ms)
50% 101
66% 108
75% 112
80% 116
90% 121
95% 3028
98% 3040
99% 3044
100% 3058 (longest request)
Estos resultados son muy consistentes en múltiples ejecuciones. Como hay otro tráfico que va a esa casilla, no estoy seguro de dónde estaría exactamente el límite, si es que hay uno, pero parece sospechosamente cercano a 256.
Naturalmente, supuse que esto estaba siendo causado por el límite de subprocesos en prefork, así que seguí adelante y ajusté la configuración para duplicar el número de subprocesos disponibles y evitar que el conjunto de subprocesos crezca y se reduzca innecesariamente:
<IfModule prefork.c>
StartServers 512
MinSpareServers 512
MaxSpareServers 512
ServerLimit 512
MaxClients 512
MaxRequestsPerChild 5000
</IfModule>
mod_status confirma que ahora estoy corriendo con 512 hilos disponibles
8 requests currently being processed, 504 idle workers
Sin embargo, intentar 265 solicitudes simultáneas aún arroja resultados casi idénticos a los anteriores.
Connection Times (ms)
min mean[+/-sd] median max
Connect: 25 211 714.7 31 3034
Processing: 17 94 28.6 103 138
Waiting: 17 93 28.5 103 138
Total: 57 306 700.8 138 3071
Percentage of the requests served within a certain time (ms)
50% 138
66% 145
75% 150
80% 161
90% 167
95% 3066
98% 3068
99% 3068
100% 3071 (longest request)
Después de buscar en la documentación (y Stack Exchange) no puedo obtener más opciones de configuración para tratar de solucionar este cuello de botella. ¿Hay algo que me estoy perdiendo? ¿Debo comenzar a buscar respuestas fuera de apache? Alguien más ha visto este comportamiento? Cualquier ayuda sería muy apreciada.
EDITAR:
Según el consejo de Ladadadada, corrí derecho contra apache. Intenté con -tt y -T varias veces y no pude encontrar nada fuera de lo común. Luego intenté ejecutar strace -c contra todos los procesos de apache actualmente en ejecución, y obtuve esto:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
22.09 0.317836 5 62128 4833 open
19.91 0.286388 4 65374 1896 lstat
13.06 0.187854 0 407433 pread
10.70 0.153862 6 27076 semop
7.88 0.113343 3 38598 poll
6.86 0.098694 1 100954 14380 read
(... resumido)
Si estoy leyendo esto correctamente (y tengan paciencia conmigo, ya que no uso strace muy a menudo), ninguna de las llamadas al sistema puede dar cuenta de la cantidad de tiempo que tardan estas solicitudes. Casi parece que el cuello de botella está ocurriendo antes de que las solicitudes lleguen incluso a los hilos de trabajo.
EDITAR 2:
Como varias personas sugirieron, volví a ejecutar la prueba en el servidor web (anteriormente la prueba se ejecutó desde una ubicación neutral de Internet). Los resultados fueron sorprendentes:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 11 6.6 12 21
Processing: 5 247 971.0 10 4204
Waiting: 3 245 971.3 7 4204
Total: 16 259 973.3 21 4225
Percentage of the requests served within a certain time (ms)
50% 21
66% 23
75% 24
80% 24
90% 26
95% 4225
98% 4225
99% 4225
100% 4225 (longest request)
El tiempo final es similar a la prueba basada en Internet, pero parece ser consistentemente un poco peor cuando se ejecuta localmente. Más interesante aún, el perfil ha cambiado dramáticamente. Mientras que antes de que la mayor parte del tiempo de las solicitudes de larga duración se gastara en "conectar", ahora el cuello de botella parece estar en proceso o en espera. Tengo que sospechar que esto realmente puede ser un problema separado que anteriormente estaba enmascarado por las limitaciones de la red.
Al ejecutar la prueba nuevamente desde otra máquina en la misma red local que el host Apache, veo resultados mucho más razonables:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 2 0.8 2 4
Processing: 13 118 99.8 205 222
Waiting: 13 118 99.7 204 222
Total: 15 121 99.7 207 225
Percentage of the requests served within a certain time (ms)
50% 207
66% 219
75% 220
80% 221
90% 222
95% 224
98% 224
99% 225
100% 225 (longest request)
Estas dos pruebas juntas plantean una serie de preguntas, pero aparte de eso, ahora hay un caso convincente para que se produzca algún tipo de cuello de botella grave en la red bajo una cierta cantidad de carga. Creo que los próximos pasos serán investigar la capa de red por separado.