El rendimiento de Apache se degrada drásticamente por encima de ~ 256 solicitudes simultáneas

14

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.

cmckendry
fuente
Opciones a considerar: CloudFlare, drupal.org/project/boost , CDN, Varnish cache.
ceejayoz
No nos está diciendo nada sobre lo que está haciendo este servidor (en el mundo real) además de atender solicitudes HTTP. ¿Existe una base de datos (o algún otro recurso común que pueda sufrir una contención de bloqueo)? Si el problema ocurre repentinamente en EXACTAMENTE 256 solicitudes (OK en 255) probablemente hay algún recurso externo que se está inundando. (Su salto en una página estática también es definitivamente anormal; consulte la respuesta de Ladadadada para obtener algunos consejos de depuración)
voretaq7
ceejayoz: Agradezco las sugerencias, pero fundamentalmente creo que Apache no debería ser tan lento. Hay muchas cosas que podemos hacer para mitigar el efecto del problema, pero preferiría solucionarlo o al menos entenderlo.
cmckendry
voretaq7: Inicialmente estaba pensando en estas mismas líneas, ya que una solicitud típica también implicaría php / mysql, pero el problema persiste en el mismo umbral incluso cuando se sirve contenido completamente estático.
cmckendry
1
¿Es este un servidor real o una VM? ¿Hace su prueba desde localhost, red local o Internet? Los tiempos de respuesta mínimos en el rango de 100 ms sugieren pruebas de Internet. Intente realizar una prueba desde localhost; tal vez su proveedor simplemente lo está estrangulando.
Tometzky

Respuestas:

4

Lo que haría en esta situación es correr

strace -f -p <PID> -tt -T -s 500 -o trace.txt

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 -tty le -Tproporcionan 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()o stat()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 que poll()estaba esperando.


Buena idea mirando la -copció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 correr stracesimultáneamente en todos los niños).

Desafortunadamente, straceno 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 abrespuesta lenta muestra que los 3000 ms se gastaron en la connectfase. 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 tcpdumpen ambos extremos de la conexión (preferiblemente con la ntpejecució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 abcarrera 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.

Ladadadada
fuente