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.
fuente
Respuestas:
Lo que haría en esta situación es correr
en uno de sus procesos de Apache durante la prueba ab hasta que capture una de las respuestas lentas. Entonces echa un vistazo
trace.txt
.Las opciones
-tt
y le-T
proporcionan marcas de tiempo del inicio y la duración de cada llamada del sistema para ayudar a identificar las lentas.Es posible que encuentre una sola llamada lenta del sistema como
open()
ostat()
o una llamada rápida con (posiblemente varias)poll()
llamadas directamente después de ella. Si encuentra uno que esté funcionando en un archivo o conexión de red (muy probablemente), mire hacia atrás a través del rastreo hasta que encuentre ese archivo o identificador de conexión. Las llamadas anteriores en ese mismo identificador deberían darle una idea de lo quepoll()
estaba esperando.Buena idea mirando la
-c
opción. ¿Se aseguró de que el niño Apache que estaba rastreando sirvió al menos una de las solicitudes lentas durante ese tiempo? (Ni siquiera estoy seguro de cómo haría esto, aparte de correrstrace
simultáneamente en todos los niños).Desafortunadamente,
strace
no nos da una idea completa de lo que está haciendo un programa en ejecución. Solo rastrea las llamadas del sistema. Pueden pasar muchas cosas dentro de un programa que no requiere pedirle nada al kernel. Para saber si esto está sucediendo, puede ver las marcas de tiempo del inicio de cada llamada al sistema. Si ve brechas significativas, ahí es donde va el tiempo. Esto no es fácil de entender y, de todos modos, siempre hay brechas pequeñas entre las llamadas del sistema.Como dijo que el uso de la CPU se mantiene bajo, probablemente no ocurran cosas excesivas entre las llamadas del sistema, pero vale la pena verificarlo.
Mirando más de cerca el resultado de
ab
:El salto repentino en los tiempos de respuesta (parece que no hay tiempos de respuesta entre 150 ms y 3000 ms) sugiere que está ocurriendo un tiempo de espera específico en algún lugar que se activa por encima de alrededor de 256 conexiones simultáneas. Se esperaría una degradación más suave si se estuviera quedando sin RAM o la CPU realiza ciclos normales de E / S.
En segundo lugar, la
ab
respuesta lenta muestra que los 3000 ms se gastaron en laconnect
fase. Casi todos tomaron alrededor de 30 ms, pero el 5% tardó 3000 ms. Esto sugiere que la red es el problema.De donde estas huyendo
ab
? ¿Puedes probarlo desde la misma red que la máquina Apache?Para obtener más información, intente ejecutar
tcpdump
en ambos extremos de la conexión (preferiblemente con lantp
ejecución en ambos extremos para poder sincronizar las dos capturas) y busque cualquier retransmisión de TCP. Wireshark es particularmente bueno para analizar los volcados porque resalta las retransmisiones tcp en un color diferente, por lo que son fáciles de encontrar.También podría valer la pena mirar los registros de cualquier dispositivo de red al que tenga acceso. Recientemente tuve un problema con uno de nuestros cortafuegos en el que podía manejar el ancho de banda en términos de kb / s pero no podía manejar la cantidad de paquetes por segundo que estaba recibiendo. Llegó a 140,000 paquetes por segundo. Algunas matemáticas rápidas en su
ab
carrera me hacen creer que habría estado viendo alrededor de 13,000 paquetes por segundo (ignorando el 5% de las solicitudes lentas). Tal vez este es el cuello de botella que has alcanzado. El hecho de que esto suceda alrededor de 256 podría ser pura coincidencia.fuente