http keep-alive en la era moderna

92

Entonces, según el autor de haproxy, quién sabe una o dos cosas sobre http:

Keep-alive se inventó para reducir el uso de CPU en servidores cuando las CPU eran 100 veces más lentas. Pero lo que no se dice es que las conexiones persistentes consumen mucha memoria y nadie las puede utilizar excepto el cliente que las abrió. Hoy en 2009, las CPU son muy baratas y la memoria todavía está limitada a unos pocos gigabytes por la arquitectura o el precio. Si un sitio necesita mantenerse activo, existe un problema real. Los sitios muy cargados a menudo deshabilitan la función Keep-Alive para admitir el número máximo de clientes simultáneos. La verdadera desventaja de no mantener vivo es una latencia ligeramente mayor para buscar objetos. Los navegadores duplican el número de conexiones simultáneas en sitios no activos para compensar esto.

(de http://haproxy.1wt.eu/ )

¿Está esto en línea con la experiencia de otras personas? es decir, sin keep-alive, ¿el resultado apenas se nota ahora? (Probablemente valga la pena señalar que con websockets, etc., una conexión se mantiene "abierta" independientemente del estado de mantener vivo de todos modos, para aplicaciones muy receptivas). ¿Es el efecto mayor para las personas que están alejadas del servidor, o si hay muchos artefactos para cargar desde el mismo host al cargar una página? (Creo que cosas como CSS, imágenes y JS provienen cada vez más de CDN compatibles con caché).

Pensamientos

(No estoy seguro si esto es una cosa de serverfault.com, pero no cruzaré la publicación hasta que alguien me diga que la mueva allí).

Michael Neale
fuente
1
Vale la pena señalar que en otras partes de la documentación de haproxy se menciona el keep-alive en otros términos más favorables. Me encantaría conocer la experiencia de las personas, especialmente para el alojamiento masivo.
Michael Neale
"¿Obtener un servidor web / de aplicaciones mejor diseñado"? :-) Los diseños más nuevos (como Jetty) con manejo de conexión de continuación (como) esencialmente mitigan los problemas de memoria / hilo. Además, "pocos GB" suena como un término de servidor 2008/2009 ;-)
3
Me suena a tonterías. El RTT adicional involucrado en la configuración de un nuevo enchufe es un límite físico estricto que, con frecuencia, es lo suficientemente largo para ser detectado por un ser humano y no puede reducirse dentro de las leyes físicas conocidas. Por el contrario, la RAM es barata, cada vez más barata y no hay razón para que un socket inactivo use más de unos pocos kB.
Will Dean
2
pero lo interesante es que esto no es solo teoría, este es el autor de haproxy. Todo lo demás que escucho son teorías y suposiciones.
Michael Neale

Respuestas:

141

Hola, ya que soy el autor de esta cita, responderé :-)

Hay dos problemas importantes en los sitios grandes: conexiones simultáneas y latencia. La conexión simultánea se debe a clientes lentos que tardan mucho en descargar contenidos y a estados de conexión inactivos. Esos estados de conexión inactivos se deben a la reutilización de la conexión para recuperar varios objetos, lo que se conoce como mantener vivo, que se incrementa aún más con la latencia. Cuando el cliente está muy cerca del servidor, puede hacer un uso intensivo de la conexión y asegurarse de que casi nunca esté inactiva. Sin embargo, cuando finaliza la secuencia, a nadie le importa cerrar rápidamente el canal y la conexión permanece abierta y sin usar durante mucho tiempo. Esa es la razón por la que muchas personas sugieren usar un tiempo de espera de vida muy bajo. En algunos servidores como Apache, el tiempo de espera más bajo que puede establecer es de un segundo y, a menudo, es demasiado para soportar cargas elevadas: si tiene 20000 clientes frente a usted y obtienen un promedio de un objeto por segundo, tendrá esas 20000 conexiones establecidas permanentemente. 20000 conexiones simultáneas en un servidor de propósito general como Apache es enorme, requerirá entre 32 y 64 GB de RAM dependiendo de los módulos que se carguen, y probablemente no pueda esperar ir mucho más alto incluso agregando RAM. En la práctica, para 20000 clientes, es posible que vea entre 40000 y 60000 conexiones simultáneas en el servidor porque los navegadores intentarán configurar de 2 a 3 conexiones si tienen muchos objetos para recuperar. y probablemente no pueda esperar ir mucho más alto incluso agregando RAM. En la práctica, para 20000 clientes, incluso puede ver entre 40000 y 60000 conexiones simultáneas en el servidor porque los navegadores intentarán configurar de 2 a 3 conexiones si tienen muchos objetos para recuperar. y probablemente no pueda esperar ir mucho más alto incluso agregando RAM. En la práctica, para 20000 clientes, incluso puede ver entre 40000 y 60000 conexiones simultáneas en el servidor porque los navegadores intentarán configurar de 2 a 3 conexiones si tienen muchos objetos para recuperar.

Si cierra la conexión después de cada objeto, la cantidad de conexiones simultáneas disminuirá drásticamente. De hecho, se reducirá en un factor correspondiente al tiempo promedio para descargar un objeto por el tiempo entre objetos. Si necesita 50 ms para descargar un objeto (una foto en miniatura, un botón, etc.), y descarga en promedio 1 objeto por segundo como se indicó anteriormente, entonces solo tendrá 0.05 de conexión por cliente, que es solo 1000 conexiones concurrentes para 20000 clientes.

Ahora va a contar el tiempo de establecer nuevas conexiones. Los clientes muy remotos experimentarán una latencia desagradable. En el pasado, los navegadores solían utilizar grandes cantidades de conexiones simultáneas cuando Keep-Alive estaba deshabilitado. Recuerdo cifras de 4 en MSIE y 8 en Netscape. Esto realmente habría dividido tanto la latencia promedio por objeto. Ahora que Keep-Alive está presente en todas partes, ya no vemos números tan altos, porque hacerlo aumenta aún más la carga en los servidores remotos y los navegadores se encargan de proteger la infraestructura de Internet.

Esto significa que con los navegadores actuales, es más difícil conseguir que los servicios que no son de mantenimiento de vida sean tan receptivos como los de mantenimiento de vida. Además, algunos navegadores (por ejemplo: Opera) usan heurística para intentar usar pipelinining. La canalización es una forma eficiente de usar Keep-Alive, porque casi elimina la latencia al enviar múltiples solicitudes sin esperar una respuesta. Lo probé en una página con 100 fotos pequeñas, y el primer acceso es aproximadamente dos veces más rápido que sin Keep-Alive, pero el siguiente acceso es aproximadamente 8 veces más rápido, porque las respuestas son tan pequeñas que solo cuenta la latencia (solo Respuestas "304").

Yo diría que, idealmente, deberíamos tener algunos parámetros optimizables en los navegadores para que mantengan vivas las conexiones entre los objetos recuperados y que lo suelten inmediatamente cuando la página esté completa. Pero, lamentablemente, no estamos viendo eso.

Por esta razón, algunos sitios que necesitan instalar servidores de propósito general como Apache en la parte frontal y que deben admitir una gran cantidad de clientes generalmente tienen que deshabilitar Keep-Alive. Y para obligar a los navegadores a aumentar el número de conexiones, utilizan varios nombres de dominio para que las descargas se puedan paralelizar. Es particularmente problemático en sitios que hacen un uso intensivo de SSL porque la configuración de la conexión es aún mayor, ya que hay un viaje de ida y vuelta adicional.

Lo que se observa más comúnmente hoy en día es que dichos sitios prefieren instalar interfaces ligeras como haproxy o nginx, que no tienen problemas para manejar decenas a cientos de miles de conexiones simultáneas, habilitan el mantenimiento de la vida en el lado del cliente y lo deshabilitan en el Lado de Apache. Por este lado, el costo de establecer una conexión es casi nulo en términos de CPU, y no se nota en absoluto en términos de tiempo. De esa manera, esto proporciona lo mejor de ambos mundos: baja latencia debido a mantener vivo con tiempos de espera muy bajos en el lado del cliente y bajo número de conexiones en el lado del servidor. Todos están felices :-)

Algunos productos comerciales mejoran aún más esto reutilizando las conexiones entre el balanceador de carga frontal y el servidor y multiplexando todas las conexiones de los clientes a través de ellos. Cuando los servidores están cerca del LB, la ganancia no es mucho mayor que la de la solución anterior, pero a menudo requerirá adaptaciones en la aplicación para garantizar que no haya riesgo de cruce de sesiones entre usuarios debido al intercambio inesperado de una conexión entre múltiples usuarios. . En teoría, esto nunca debería suceder. La realidad es muy diferente :-)

Willy Tarreau
fuente
1
¡Gracias por la respuesta completa y completa! Estaba un poco confundido por varios comentarios en la página sobre Keep-Alive, pero todo esto tiene sentido.
Michael Neale
Curiosamente, he observado que Chrome en Linux reutiliza una conexión mantenida en vivo durante bastantes segundos, es decir, el tiempo que tardó en abrir otra pestaña, esta otra pestaña era para un nombre de host diferente, pero se resolvió a través de un comodín de DNS para el mismo servidor (masivo alojamiento virtual), ¡y por lo tanto reutilizó la misma conexión! (esto me causó alguna sorpresa, no del tipo bueno; obviamente, si mantener vivo es solo del lado del cliente, está bien).
Michael Neale
Todo lo que escuché fue "use cualquier otra cosa que no sea apache y no es gran cosa". Lo que extrapolé fue "deshabilitar mod_php y pasajero y luego incluso apache podría tener una oportunidad de luchar".
coolaj86
@ CoolAJ86: el punto es absolutamente no golpear Apache, y personalmente lo uso. El punto es que cuanto más genérico es el servidor, menos opciones tiene para escalar. Algunos módulos requieren el modelo previo a la bifurcación, por lo que no se puede escalar a una gran cantidad de conexiones. Pero como se explicó, no es gran cosa, ya que puede combinarlo con otro componente gratuito como haproxy. ¿Por qué alguien reemplazaría todo en este caso? ¡Es mejor instalar haproxy que pasar por la molestia de volver a implementar su aplicación usando otro servidor!
Willy Tarreau
22

En los años transcurridos desde que se escribió esto (y se publicó aquí en stackoverflow) ahora tenemos servidores como nginx que están ganando popularidad.

nginx, por ejemplo, puede mantener abiertas 10,000 conexiones Keep-Alive en un solo proceso con solo 2.5 MB (megabytes) de RAM. De hecho, es fácil mantener abiertas varios miles de conexiones con muy poca RAM, y los únicos límites que alcanzará serán otros límites, como el número de identificadores de archivos abiertos o conexiones TCP.

Keep-alive fue un problema, no por ningún problema con la especificación de keep-alive en sí, sino por el modelo de escalamiento basado en procesos de Apache y por el hackeo de keep-alives en un servidor cuya arquitectura no fue diseñada para adaptarse a él.

Especialmente problemático es Apache Prefork + mod_php + keep-alives. Este es un modelo en el que cada conexión continuará ocupando toda la RAM que ocupa un proceso PHP, incluso si está completamente inactivo y solo permanece abierto como mantenimiento. Esto no es escalable. Pero los servidores no tienen que diseñarse de esta manera: no hay ninguna razón en particular que un servidor necesite mantener cada conexión de mantenimiento en un proceso separado (especialmente no cuando cada uno de esos procesos tiene un intérprete PHP completo). PHP-FPM y un modelo de procesamiento de servidor basado en eventos como el de nginx resuelven el problema con elegancia.

Actualización 2015:

SPDY y HTTP / 2 reemplazan la funcionalidad de mantenimiento de HTTP con algo aún mejor: la capacidad no solo de mantener viva una conexión y realizar múltiples solicitudes y respuestas a través de ella, sino de que se multiplexen, por lo que las respuestas se pueden enviar en cualquier orden , y en paralelo, en lugar de solo en el orden en que se solicitaron. Esto evita que las respuestas lentas bloqueen las más rápidas y elimina la tentación de los navegadores de mantener abiertas varias conexiones paralelas a un solo servidor. Estas tecnologías resaltan aún más las deficiencias del enfoque mod_php y los beneficios de algo así como un servidor web basado en eventos (o al menos, multiproceso) acoplado por separado con algo como PHP-FPM.

thomasrutter
fuente
2

mi entendimiento era que tenía poco que ver con la CPU, pero la latencia en la apertura de sockets repetidos al otro lado del mundo. incluso si tiene un ancho de banda infinito, la latencia de conexión ralentizará todo el proceso. amplificado si su página tiene docenas de objetos. incluso una conexión persistente tiene una latencia de solicitud / respuesta, pero se reduce cuando tiene 2 sockets, ya que en promedio, uno debería estar transmitiendo datos mientras que el otro podría estar bloqueando. Además, un enrutador nunca asumirá que un conector se conecta antes de permitirle escribir en él. Necesita el apretón de manos completo de ida y vuelta. de nuevo, no pretendo ser un experto, pero así es como siempre lo vi. lo que realmente sería genial es un protocolo completamente ASYNC (no, no un protocolo completamente enfermo).

catchpolenet
fuente
sí, esa sería mi suposición. tal vez sea una compensación: hay un punto en el que la latencia (debido a la distancia) significa que es un problema real
Michael Neale
ok, entonces la tipografía moderna te haría conectarte a un proxy que está cerca (tal vez). ¿pero luego extiende la pregunta a si los proxies deberían usar conexiones persistentes?
catchpolenet
@Michael Neale también, debido a cosas como el inicio lento de TCP, la penalización de latencia real es mucho peor de lo que cabría esperar.
MartinodF
tal vez la compensación sea un período de tiempo de espera mucho más corto. Si tiene copias de seguridad de las solicitudes, ¿por qué apagar el socket y comenzar de nuevo? incluso 1 segundo permitiría que una página se cargue con persistencia total y luego apagar los sockets inmediatamente después.
catchpolenet
2

Los mantenimientos activos muy largos pueden ser útiles si está utilizando una CDN de "extracción de origen" como CloudFront o CloudFlare. De hecho, esto puede resultar más rápido que ningún CDN, incluso si está ofreciendo contenido completamente dinámico.

Si ha estado activo durante mucho tiempo de modo que cada PoP básicamente tiene una conexión permanente a su servidor, entonces la primera vez que los usuarios visitan su sitio, pueden hacer un protocolo de enlace TCP rápido con su PoP local en lugar de un apretón de manos lento con usted. (La luz en sí misma tarda alrededor de 100 ms en recorrer la mitad del mundo a través de fibra, y establecer una conexión TCP requiere que se pasen tres paquetes de un lado a otro. SSL requiere tres viajes de ida y vuelta ).

mjs
fuente
1
Tuve la tentación de hacer +1, pero luego su segundo párrafo tiene esta observación incorrecta de que la luz solo tarda 10 ms en viajar por la mitad del mundo. 10 m de velocidad de la luz en vacío son 3000 km y 10 m de velocidad de la luz en una fibra no son mucho más de 2000 km; la mitad del camino alrededor del mundo (a lo largo de la superficie) son 20.000 km. Entonces eso sería 100 ms --- si solo su fibra fuera directamente de Londres a Sydney en lugar de circunnavegar África por mar o tomar la ruta larga por Hawai ...
pirámides
@pyramids Tienes razón, o escribí eso o simplemente me equivoqué. Actualizará.
mjs