BLOG | NGINX

Dentro de NGINX: Cómo diseñamos para el rendimiento y la escalabilidad

NGINX - Parte de F5 - horizontal, negro, tipo RGB
Miniatura de Owen Garrett
Owen Garrett
Publicado el 10 de junio de 2015

NGINX lidera el grupo en cuanto a rendimiento web, y todo se debe a la forma en que está diseñado el software. Mientras que muchos servidores web y servidores de aplicação utilizan una arquitectura simple basada en procesos o subprocesos, NGINX se destaca con una sofisticada arquitectura impulsada por eventos que le permite escalar a cientos de miles de conexiones simultáneas en hardware moderno.

La infografía Inside NGINX profundiza en la arquitectura de procesos de alto nivel para ilustrar cómo NGINX maneja múltiples conexiones dentro de un solo proceso. Este blog explica cómo funciona todo con más detalle.

Preparando el escenario: el modelo de proceso NGINX

Para comprender mejor este diseño, es necesario comprender cómo funciona NGINX. NGINX tiene un proceso maestro (que realiza operaciones privilegiadas como leer la configuración y vincularse a los puertos) y una serie de procesos de trabajo y auxiliares.

# servicio nginx restart * Reiniciando nginx # ps -ef --forest | grep nginx root 32475 1 0 13:36 ?        00:00:00 nginx: proceso maestro /usr/sbin/nginx -c /etc/nginx/nginx.conf nginx 32476 32475 0 13:36 ?        00:00:00 _ nginx: proceso de trabajo nginx 32477 32475 0 13:36 ?        00:00:00 _ nginx: proceso de trabajo nginx 32479 32475 0 13:36 ?        00:00:00 _ nginx: proceso de trabajo nginx 32480 32475 0 13:36 ?        00:00:00 _ nginx: proceso de trabajo nginx 32481 32475 0 13:36 ?        00:00:00 _ nginx: proceso del administrador de caché nginx 32482 32475 0 13:36 ?        00:00:00 _ nginx: proceso de carga de caché

En este servidor de cuatro núcleos, el proceso maestro NGINX crea cuatro procesos de trabajo y un par de procesos auxiliares de caché que administran el caché de contenido en disco.

¿Por qué es importante la arquitectura?

La base fundamental de cualquier aplicação Unix es el hilo o proceso. (Desde la perspectiva del sistema operativo Linux, los subprocesos y los procesos son prácticamente idénticos; la principal diferencia es el grado en que comparten memoria). Un hilo o proceso es un conjunto autónomo de instrucciones que el sistema operativo puede programar para ejecutarse en un núcleo de CPU. La mayoría de las aplicações complejas ejecutan múltiples subprocesos o procesos en paralelo por dos razones:

  • Pueden utilizar más núcleos de cómputo al mismo tiempo.
  • Los subprocesos y los procesos hacen que sea muy fácil realizar operaciones en paralelo (por ejemplo, manejar múltiples conexiones al mismo tiempo).

Los procesos y los subprocesos consumen recursos. Cada uno utiliza memoria y otros recursos del sistema operativo, y es necesario intercambiarlos dentro y fuera de los núcleos (una operación llamada cambio de contexto ). La mayoría de los servidores modernos pueden manejar cientos de pequeños subprocesos o procesos activos simultáneamente, pero el rendimiento se degrada seriamente una vez que se agota la memoria o cuando una alta carga de E/S provoca un gran volumen de cambios de contexto.

La forma común de diseñar aplicações de red es asignar un hilo o proceso a cada conexión. Esta arquitectura es simple y fácil de implementar, pero no escala cuando la aplicação necesita manejar miles de conexiones simultáneas.

¿Cómo funciona NGINX?

NGINX utiliza un modelo de proceso predecible que se adapta a los recursos de hardware disponibles:

  • El proceso maestro realiza operaciones privilegiadas como leer la configuración y vincularse a los puertos, y luego crea una pequeña cantidad de procesos secundarios (los siguientes tres tipos).
  • El proceso del cargador de caché se ejecuta al inicio para cargar el caché basado en disco en la memoria y luego sale. Está programado de forma conservadora, por lo que sus demandas de recursos son bajas.
  • El proceso del administrador de caché se ejecuta periódicamente y elimina las entradas de los cachés de disco para mantenerlos dentro de los tamaños configurados.
  • ¡Los procesos de trabajo hacen todo el trabajo! Manejan conexiones de red, leen y escriben contenido en el disco y se comunican con servidores ascendentes.

La configuración de NGINX recomendada en la mayoría de los casos (ejecutar un proceso de trabajo por núcleo de CPU) permite el uso más eficiente de los recursos de hardware. Lo configuras estableciendo el parámetro auto en la directivaworker_processes :

procesos_de_trabajo automáticos;

Cuando un servidor NGINX está activo, solo los procesos de trabajo están ocupados. Cada proceso de trabajo maneja múltiples conexiones de manera no bloqueante, lo que reduce la cantidad de cambios de contexto.

Cada proceso de trabajo es de un solo subproceso y se ejecuta de forma independiente, capturando nuevas conexiones y procesándolas. Los procesos pueden comunicarse utilizando memoria compartida para datos de caché compartidos, datos de persistencia de sesión y otros recursos compartidos.

Dentro del proceso de trabajo de NGINX

El proceso de trabajo NGINX es un motor sin bloqueos, basado en eventos, para procesar solicitudes de clientes web.

Cada proceso de trabajo de NGINX se inicializa con la configuración de NGINX y el proceso maestro le proporciona un conjunto de sockets de escucha.

Los procesos de trabajo de NGINX comienzan esperando eventos en los sockets de escucha ( accept_mutex y fragmentación del socket del kernel ). Los eventos se inician mediante nuevas conexiones entrantes. Estas conexiones se asignan a una máquina de estados : la máquina de estados HTTP es la más comúnmente utilizada, pero NGINX también implementa máquinas de estados para el tráfico de flujo (TCP sin procesar) y para varios protocolos de correo (SMTP, IMAP y POP3).

Para procesar una solicitud entrante de cliente, NGINX lee los encabezados HTTP, aplica límites si están configurados, realiza redirecciones internas y subsolicitudes según sea necesario, reenvía a servicios de backend, aplica filtros y registra sus acciones.

La máquina de estados es esencialmente el conjunto de instrucciones que le indican a NGINX cómo procesar una solicitud. La mayoría de los servidores web que realizan las mismas funciones que NGINX utilizan una máquina de estados similar; la diferencia radica en la implementación.

Programación de la máquina de estados

Piense en la máquina de estados como si fuera las reglas del ajedrez. Cada transacción HTTP es un juego de ajedrez. En un lado del tablero de ajedrez está el servidor web: un gran maestro que puede tomar decisiones muy rápidamente. En el otro lado está el cliente remoto: el navegador web que accede al sitio o a la aplicação a través de una red relativamente lenta.

Sin embargo, las reglas del juego pueden ser muy complicadas. Por ejemplo, es posible que el servidor web necesite comunicarse con otras partes (mediante un proxy a una aplicação ascendente) o hablar con un servidor de autenticación. Los módulos de terceros en el servidor web pueden incluso ampliar las reglas del juego.

Una máquina de estados de bloqueo

Recuerde nuestra descripción de un proceso o hilo como un conjunto autónomo de instrucciones que el sistema operativo puede programar para ejecutarse en un núcleo de CPU. La mayoría de los servidores y aplicações web utilizan un modelo de proceso por conexión o de hilo por conexión para jugar al ajedrez. Cada proceso o hilo contiene las instrucciones para jugar un juego hasta el final. Durante el tiempo que el servidor ejecuta el proceso, éste pasa la mayor parte del tiempo "bloqueado", esperando a que el cliente complete su siguiente movimiento.

  1. El proceso del servidor web escucha nuevas conexiones (nuevos juegos iniciados por los clientes) en los sockets de escucha.
  2. Cuando recibe un nuevo juego, juega ese juego, bloqueándose después de cada movimiento para esperar la respuesta del cliente.
  3. Una vez que se completa el juego, el proceso del servidor web puede esperar para ver si el cliente desea iniciar un nuevo juego (esto corresponde a una conexión keepalive). Si se cierra la conexión (el cliente desaparece o se produce un tiempo de espera), el proceso del servidor web vuelve a escuchar nuevos juegos.

El punto importante a recordar es que cada conexión HTTP activa (cada partida de ajedrez) requiere un proceso o hilo dedicado (un gran maestro). Esta arquitectura es simple y fácil de ampliar con módulos de terceros ('nuevas reglas'). Sin embargo, hay un enorme desequilibrio: la conexión HTTP, bastante liviana, representada por un descriptor de archivo y una pequeña cantidad de memoria, se asigna a un hilo o proceso separado, un objeto de sistema operativo muy pesado. Es una comodidad de programación, pero implica un enorme desperdicio.

NGINX es un verdadero gran maestro

¿Quizás hayas oído hablar de las partidas de exhibición simultáneas , en las que un gran maestro de ajedrez juega contra docenas de oponentes al mismo tiempo?

Kiril Georgiev
Kiril Georgiev interpretó a 360 personas simultáneamente en Sofía, Bulgaria. Su resultado final fue de 284 victorias, 70 empates y 6 derrotas.

Así es como un proceso de trabajo NGINX juega al “ajedrez”. Cada trabajador (recuerde: generalmente hay un trabajador por cada núcleo de CPU) es un gran maestro que puede jugar cientos (de hecho, cientos de miles) de juegos simultáneamente.

  1. El trabajador espera eventos en los sockets de escucha y conexión.
  2. Los eventos ocurren en los sockets y el trabajador los maneja:
    • Un evento en el socket de escucha significa que un cliente ha iniciado un nuevo juego de ajedrez. El trabajador crea un nuevo socket de conexión.
    • Un evento en un socket de conexión significa que el cliente ha realizado un nuevo movimiento. El trabajador responde con prontitud.

Un trabajador nunca bloquea el tráfico de la red, esperando que su “oponente” (el cliente) responda. Una vez realizado su movimiento, el trabajador procede inmediatamente a otros juegos en los que hay movimientos pendientes de ser procesados, o da la bienvenida a nuevos jugadores en la puerta.

¿Por qué es más rápido que una arquitectura multiproceso con bloqueo?

NGINX se escala muy bien para soportar cientos de miles de conexiones por proceso de trabajo. Cada nueva conexión crea otro descriptor de archivo y consume una pequeña cantidad de memoria adicional en el proceso de trabajo. Hay muy poca sobrecarga adicional por conexión. Los procesos NGINX pueden permanecer anclados a las CPU. Los cambios de contexto son relativamente poco frecuentes y ocurren cuando no hay trabajo que hacer.

En el enfoque de bloqueo, de conexión por proceso, cada conexión requiere una gran cantidad de recursos adicionales y sobrecarga, y los cambios de contexto (intercambio de un proceso a otro) son muy frecuentes.

Para obtener una explicación más detallada, consulte este artículo sobre la arquitectura NGINX, de Andrew Alexeev, vicepresidente de Desarrollo Corporativo y cofundador de NGINX, Inc.

Con un ajuste de sistema adecuado, NGINX puede escalar para manejar cientos de miles de conexiones HTTP simultáneas por proceso de trabajo y puede absorber picos de tráfico (una afluencia de juegos nuevos) sin perder el ritmo.

Actualización de la configuración y actualización de NGINX

La arquitectura de procesos de NGINX, con una pequeña cantidad de procesos de trabajo, permite una actualización muy eficiente de la configuración e incluso del propio binario de NGINX.

NGINX recarga su configuración sin ningún tiempo de inactividad (interrupción del procesamiento de la solicitud).

Actualizar la configuración de NGINX es una operación muy simple, liviana y confiable. Por lo general, solo significa ejecutar el comando nginx -s reload , que verifica la configuración en el disco y envía una señal SIGHUP al proceso maestro.

Cuando el proceso maestro recibe un SIGHUP, hace dos cosas:

  1. Recarga la configuración y bifurca un nuevo conjunto de procesos de trabajo. Estos nuevos procesos de trabajo comienzan inmediatamente a aceptar conexiones y a procesar tráfico (utilizando las nuevas configuraciones).
  2. Indica a los antiguos procesos de trabajo que deben salir sin problemas. Los procesos de trabajo dejan de aceptar nuevas conexiones. Tan pronto como se completa cada solicitud HTTP actual, el proceso de trabajo cierra limpiamente la conexión (es decir, no hay conexiones persistentes). Una vez cerradas todas las conexiones, los procesos de trabajo salen.

Este proceso de recarga puede provocar un pequeño aumento en el uso de CPU y memoria, pero generalmente es imperceptible en comparación con la carga de recursos de las conexiones activas. Puede recargar la configuración varias veces por segundo (y muchos usuarios de NGINX hacen exactamente eso). Muy raramente, surgen problemas cuando hay muchas generaciones de procesos de trabajo NGINX esperando que se cierren las conexiones, pero incluso estos se resuelven rápidamente.

El proceso de actualización binaria de NGINX logra el Santo Grial de la alta disponibilidad: puede actualizar el software sobre la marcha, sin caídas de conexión, tiempos de inactividad o interrupciones del servicio.

NGINX recarga su binario sin ningún tiempo de inactividad (interrupción del procesamiento de la solicitud).

El proceso de actualización binaria es similar en su enfoque a la recarga elegante de la configuración. Un nuevo proceso maestro NGINX se ejecuta en paralelo con el proceso maestro original y comparten los sockets de escucha. Ambos procesos están activos y sus respectivos procesos de trabajo manejan el tráfico. Luego puedes hacer una señal al viejo maestro y a sus trabajadores para que salgan con gracia.

El proceso completo se describe con más detalle en Controlar NGINX .

CONCLUSIÓN

La infografía Inside NGINX proporciona una descripción general de alto nivel de cómo funciona NGINX, pero detrás de esta simple explicación hay más de diez años de innovación y optimización que permiten a NGINX ofrecer el mejor rendimiento posible en una amplia gama de hardware manteniendo la seguridad y confiabilidad que requieren las aplicações web modernas.

Si desea leer más sobre las optimizaciones en NGINX, consulte estos excelentes recursos:


"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.