Rendimiento de las aplicaciones de red Linux. Introducción
Las aplicaciones web ahora se utilizan en todas partes y, entre todos los protocolos de transporte, HTTP ocupa la mayor parte. Al estudiar los matices del desarrollo de aplicaciones web, la mayoría de la gente presta muy poca atención al sistema operativo donde realmente se ejecutan estas aplicaciones. La separación de desarrollo (Dev) y operaciones (Ops) sólo empeoró la situación. Pero con el auge de la cultura DevOps, los desarrolladores se están volviendo responsables de ejecutar sus aplicaciones en la nube, por lo que les resulta muy útil familiarizarse a fondo con el backend del sistema operativo. Esto es especialmente útil si intenta implementar un sistema para miles o decenas de miles de conexiones simultáneas.
Las limitaciones de los servicios web son muy similares a las de otras aplicaciones. Ya sean balanceadores de carga o servidores de bases de datos, todas estas aplicaciones tienen problemas similares en un entorno de alto rendimiento. Comprender estas limitaciones fundamentales y cómo superarlas en general le ayudará a evaluar el rendimiento y la escalabilidad de sus aplicaciones web.
Estoy escribiendo esta serie de artículos en respuesta a preguntas de jóvenes desarrolladores que desean convertirse en arquitectos de sistemas bien informados. Es imposible comprender claramente las técnicas de optimización de aplicaciones de Linux sin profundizar en los conceptos básicos de cómo funcionan a nivel del sistema operativo. Aunque hay muchos tipos de aplicaciones, en esta serie quiero explorar aplicaciones basadas en web en lugar de aplicaciones de escritorio como un navegador o un editor de texto. Este material está destinado a desarrolladores y arquitectos que desean comprender cómo funcionan los programas Linux o Unix y cómo estructurarlos para lograr un alto rendimiento.
linux es Cuarto de servicio sistema operativo y, en la mayoría de los casos, sus aplicaciones se ejecutan en este sistema operativo. Aunque digo "Linux", la mayoría de las veces puedes asumir con seguridad que me refiero a todos los sistemas operativos tipo Unix en general. Sin embargo, no he probado el código adjunto en otros sistemas. Entonces, si está interesado en FreeBSD u OpenBSD, sus resultados pueden variar. Cuando pruebo algo específico de Linux, lo señalo.
Si bien puedes utilizar este conocimiento para crear una aplicación desde cero y estará perfectamente optimizada, es mejor no hacerlo. Si escribe un nuevo servidor web en C o C++ para la aplicación empresarial de su organización, este puede ser su último día de trabajo. Sin embargo, conocer la estructura de estas aplicaciones le ayudará a elegir los programas existentes. Podrá comparar sistemas basados en procesos con sistemas basados en subprocesos y con sistemas basados en eventos. Comprenderá y apreciará por qué Nginx funciona mejor que Apache httpd, por qué una aplicación Python basada en Tornado puede servir a más usuarios en comparación con una aplicación Python basada en Django.
ZeroHTTPd: herramienta de aprendizaje
CeroHTTPd es un servidor web que escribí desde cero en C como herramienta de enseñanza. No tiene dependencias externas, incluido el acceso a Redis. Ejecutamos nuestros propios procedimientos de Redis. Consulte a continuación para obtener más detalles.
Aunque podríamos discutir la teoría extensamente, no hay nada mejor que escribir código, ejecutarlo y comparar todas las arquitecturas de servidores entre sí. Este es el método más obvio. Por lo tanto, escribiremos un servidor web ZeroHTTPd simple utilizando cada modelo: basado en procesos, basado en subprocesos y basado en eventos. Revisemos cada uno de estos servidores y veamos cómo funcionan en comparación entre sí. ZeroHTTPd se implementa en un único archivo C. El servidor basado en eventos incluye utash, una excelente implementación de tabla hash que viene en un único archivo de encabezado. En otros casos no existen dependencias, para no complicar el proyecto.
Hay muchos comentarios en el código para ayudarlo a comprender. Al ser un servidor web simple en unas pocas líneas de código, ZeroHTTPd es también un marco mínimo para el desarrollo web. Tiene una funcionalidad limitada, pero es capaz de servir archivos estáticos y páginas "dinámicas" muy simples. Debo decir que ZeroHTTPd es bueno para aprender a crear aplicaciones Linux de alto rendimiento. En general, la mayoría de los servicios web esperan solicitudes, las verifican y las procesan. Esto es exactamente lo que hará ZeroHTTPd. Esta es una herramienta para aprender, no para producir. No es muy bueno en el manejo de errores y es poco probable que cuente con las mejores prácticas de seguridad (oh, sí, usé strcpy) o los ingeniosos trucos del lenguaje C. Pero espero que haga bien su trabajo.
Página de inicio de ZeroHTTPd. Puede generar diferentes tipos de archivos, incluidas imágenes.
Solicitud de libro de visitas
Las aplicaciones web modernas no suelen limitarse a archivos estáticos. Tienen interacciones complejas con varias bases de datos, cachés, etc. Por lo tanto, crearemos una aplicación web sencilla llamada "Libro de visitas" donde los visitantes dejan entradas bajo sus nombres. El libro de visitas almacena las entradas dejadas anteriormente. También hay un contador de visitantes en la parte inferior de la página.
Aplicación web "Libro de visitas" ZeroHTTPd
El contador de visitantes y las entradas del libro de visitas se almacenan en Redis. Para las comunicaciones con Redis se implementan procedimientos propios, no dependen de la biblioteca externa. No soy un gran partidario de implementar código casero cuando hay soluciones disponibles públicamente y bien probadas. Pero el propósito de ZeroHTTPd es estudiar el rendimiento de Linux y el acceso a servicios externos, mientras que atender solicitudes HTTP tiene un impacto grave en el rendimiento. Debemos controlar totalmente las comunicaciones con Redis en cada una de nuestras arquitecturas de servidores. En algunas arquitecturas utilizamos llamadas de bloqueo, en otras utilizamos procedimientos basados en eventos. El uso de una biblioteca cliente de Redis externa no proporcionará este control. Además, nuestro pequeño cliente Redis solo realiza algunas funciones (obtener, configurar e incrementar una clave; obtener y agregar a una matriz). Además, el protocolo Redis es extremadamente elegante y sencillo. Ni siquiera necesitas enseñarlo especialmente. El mero hecho de que el protocolo haga todo el trabajo en unas cien líneas de código demuestra lo bien pensado que está.
La siguiente figura muestra lo que hace la aplicación cuando el cliente (navegador) solicita /guestbookURL.
Cómo funciona la aplicación del libro de visitas
Cuando es necesario publicar una página del libro de visitas, hay una llamada al sistema de archivos para leer la plantilla en la memoria y tres llamadas de red a Redis. El archivo de plantilla contiene la mayor parte del contenido HTML de la página en la captura de pantalla anterior. También hay marcadores de posición especiales para la parte dinámica del contenido: publicaciones y contador de visitantes. Los recibimos de Redis, los insertamos en la página y proporcionamos al cliente el contenido completo. La tercera llamada a Redis se puede evitar porque Redis devuelve el nuevo valor clave cuando se incrementa. Sin embargo, para nuestro servidor, que tiene una arquitectura asincrónica basada en eventos, muchas llamadas de red son una buena prueba con fines de aprendizaje. Entonces descartamos el valor de retorno de Redis del número de visitantes y lo consultamos con una llamada separada.
Arquitecturas de servidor ZeroHTTPd
Estamos creando siete versiones de ZeroHTTPd con la misma funcionalidad pero con diferentes arquitecturas:
Iterativo
Servidor fork (un proceso hijo por solicitud)
Servidor previo a la bifurcación (prebifurcación de procesos)
Servidor con hilos de ejecución (un hilo por solicitud)
Servidor con creación previa al hilo
Basado en arquitectura poll()
Basado en arquitectura epoll
Medimos el rendimiento de cada arquitectura cargando el servidor con solicitudes HTTP. Pero al comparar arquitecturas altamente paralelas, el número de consultas aumenta. Probamos tres veces y calculamos el promedio.
Metodología de prueba
Configuración de prueba de carga ZeroHTTPd
Es importante que al ejecutar pruebas, no todos los componentes se ejecuten en la misma máquina. En este caso, el sistema operativo incurre en una sobrecarga de programación adicional ya que los componentes compiten por la CPU. Medir la sobrecarga del sistema operativo de cada una de las arquitecturas de servidor seleccionadas es uno de los objetivos más importantes de este ejercicio. Agregar más variables será perjudicial para el proceso. Por lo tanto, la configuración de la imagen de arriba funciona mejor.
¿Qué hace cada uno de estos servidores?
load.unixism.net: Aquí es donde ejecutamos ab, utilidad Apache Benchmark. Genera la carga necesaria para probar nuestras arquitecturas de servidores.
nginx.unixism.net: A veces queremos ejecutar más de una instancia de un programa de servidor. Para hacer esto, el servidor Nginx con la configuración adecuada funciona como un equilibrador de carga proveniente de ab a los procesos de nuestro servidor.
zerohttpd.unixism.net: aquí ejecutamos nuestros programas de servidor en siete arquitecturas diferentes, una a la vez.
redis.unixism.net: este servidor ejecuta el demonio Redis, donde se almacenan las entradas del libro de visitas y los contadores de visitantes.
Todos los servidores se ejecutan en el mismo núcleo de procesador. La idea es evaluar el máximo rendimiento de cada arquitectura. Dado que todos los programas de servidor se prueban en el mismo hardware, esta es una base de referencia para la comparación. Mi configuración de prueba consta de servidores virtuales alquilados a Digital Ocean.
¿Qué estamos midiendo?
Puedes medir diferentes indicadores. Evaluamos el rendimiento de cada arquitectura en una configuración determinada cargando los servidores con solicitudes en diferentes niveles de paralelismo: la carga crece de 20 a 15 usuarios simultáneos.
Resultados de la prueba
El siguiente gráfico muestra el rendimiento de servidores en diferentes arquitecturas en diferentes niveles de paralelismo. El eje y es el número de solicitudes por segundo, el eje x son las conexiones paralelas.
A continuación se muestra una tabla con los resultados.
En el gráfico y la tabla se puede ver que por encima de 8000 solicitudes simultáneas solo nos quedan dos jugadores: pre-fork y epoll. A medida que aumenta la carga, un servidor basado en encuestas funciona peor que uno de transmisión. La arquitectura de creación previa de subprocesos es un competidor digno de epoll, un testimonio de lo bien que el kernel de Linux programa una gran cantidad de subprocesos.
Código fuente ZeroHTTPd
Código fuente ZeroHTTPd aquí. Hay un directorio separado para cada arquitectura.
Además de siete directorios para todas las arquitecturas, hay dos más en el directorio de nivel superior: público y plantillas. El primero contiene el archivo index.html y la imagen de la primera captura de pantalla. Puede colocar otros archivos y carpetas allí, y ZeroHTTPd debería servir esos archivos estáticos sin ningún problema. Si la ruta en el navegador coincide con la ruta en la carpeta pública, ZeroHTTPd busca el archivo index.html en este directorio. El contenido del libro de visitas se genera dinámicamente. Sólo tiene una página de inicio y su contenido se basa en el archivo 'templates/guestbook/index.html'. ZeroHTTPd agrega fácilmente páginas dinámicas para extensión. La idea es que los usuarios puedan agregar plantillas a este directorio y ampliar ZeroHTTPd según sea necesario.
Para construir los siete servidores, ejecute make all desde el directorio de nivel superior, y todas las compilaciones aparecerán en este directorio. Los archivos ejecutables buscan los directorios público y de plantillas en el directorio desde el que se inician.
API de Linux
No es necesario conocer bien la API de Linux para comprender la información de esta serie de artículos. Sin embargo, recomiendo leer más sobre este tema, hay muchos recursos de referencia en Internet. Aunque abordaremos varias categorías de API de Linux, nos centraremos principalmente en procesos, subprocesos, eventos y la pila de red. Además de libros y artículos sobre la API de Linux, también recomiendo leer mana para conocer las llamadas al sistema y las funciones de biblioteca utilizadas.
Rendimiento y escalabilidad
Una nota sobre el rendimiento y la escalabilidad. En teoría, no existe ninguna conexión entre ellos. Puedes tener un servicio web que funciona muy bien, con un tiempo de respuesta de unos pocos milisegundos, pero no escala en absoluto. Del mismo modo, puede haber una aplicación web con un rendimiento deficiente que tarde unos segundos en responder, pero escala decenas para manejar decenas de miles de usuarios simultáneos. Sin embargo, la combinación de alto rendimiento y escalabilidad es una combinación muy poderosa. Las aplicaciones de alto rendimiento generalmente utilizan recursos con moderación y, por lo tanto, atienden de manera eficiente a más usuarios simultáneos en el servidor, lo que reduce los costos.
Tareas de CPU y E/S
Finalmente, en informática siempre hay dos tipos posibles de tareas: para E/S y CPU. Recibir solicitudes a través de Internet (E/S de red), servir archivos (E/S de red y disco), comunicarse con la base de datos (E/S de red y disco) son todas actividades de E/S. Algunas consultas de bases de datos pueden consumir un poco de CPU (clasificar, promediar un millón de resultados, etc.). La mayoría de las aplicaciones web están limitadas por la máxima E/S posible y el procesador rara vez se utiliza a plena capacidad. Cuando ve que alguna tarea de E/S utiliza mucha CPU, lo más probable es que sea un signo de una arquitectura de aplicación deficiente. Esto puede significar que los recursos de la CPU se desperdician en la gestión de procesos y el cambio de contexto, y esto no es del todo útil. Si está haciendo algo como procesamiento de imágenes, conversión de archivos de audio o aprendizaje automático, entonces la aplicación requiere potentes recursos de CPU. Pero para la mayoría de las aplicaciones este no es el caso.
Obtenga más información sobre las arquitecturas de servidores