Configuro NGINX Worker de tal forma que worker_processes, worker_connections y worker_rlimit_nofile coincidan exactamente y Epoll funcione en el bucle de eventos. De este modo, utilizo Núcleos de CPU Eficiente, permite escalar de forma planificada las conexiones simultáneas y mantiene bajas las latencias en los picos de carga.
Puntos centrales
Los siguientes aspectos fundamentales te servirán de guía inmediata para configurar un worker de NGINX resistente.
- procesos_trabajadores vincularlo al número de núcleos lógicos, idealmente con „auto“.
- conexiones_trabajadores ajustarlo de manera que se cubran holgadamente los picos reales.
- rlimit_nofile y aumentar los límites del sistema operativo en función del volumen de tráfico.
- epoll y activar multi_accept para aprovechar de forma eficiente el bucle de eventos.
- Pruebas de carga avanzar y realizar pequeños ajustes.
Arquitectura de NGINX: entender los servidores «master» y «worker»
Separo las tareas de Maestro Y los «worker» lo tienen claro: el «master» carga las configuraciones, abre sockets e inicia procesos, mientras que los «worker» procesan las solicitudes en el bucle de eventos. Cada «worker» funciona de forma autónoma, reacciona a los eventos y puede gestionar miles de conexiones sin generar bloqueos. Este modelo da lo mejor de sí cuando asigno adecuadamente los núcleos de la CPU y aprovecho al máximo el bucle de eventos mediante epoll. Tengo en cuenta que cada salto de proxy adicional consume recursos de conexión, lo que se refleja en los límites. Quien comprenda estas funciones, tomará decisiones conscientes sobre Recursos y evita los cuellos de botella de forma precoz.
Combinar correctamente las tres directrices clave
Considero que procesos_trabajadores, worker_connections y worker_rlimit_nofile nunca se ajustan de forma aislada, sino como un todo. El número total de conexiones posibles se calcula multiplicando el número de trabajadores por el número de conexiones por trabajador; a partir de ahí deduzco los límites para los descriptores de archivo. Si estos parámetros no están bien ajustados, me encuentro con el error „too many open files“ o sufro tiempos de espera excesivos. Para cargas elevadas necesito una cadena coherente: suficientes procesos, un número generoso de conexiones, un valor de rlimit_nofile adecuadamente aumentado y los parámetros adecuados del sistema operativo. Así evito que un valor demasiado pequeño Límite ha mermado toda la capacidad.
worker_processes: Seleccionar el número adecuado
He puesto procesos_trabajadores Por lo general, se configura en „auto“, para que NGINX detecte el número de núcleos lógicos de la CPU y pueda aprovechar cada uno de ellos. Un trabajador por núcleo evita cambios de contexto innecesarios y distribuye la carga de forma óptima, lo que permite predecir el tiempo de respuesta. En máquinas con un gran número de núcleos, pruebo deliberadamente con un número menor de trabajadores para comparar las aciertos de caché y la utilización de los núcleos. Si las métricas indican que los núcleos están sobrecargados o que aumentan las faltas de TLB, ajusto el número de trabajadores de forma gradual. Primero medir, luego cambiar: así me aseguro unos resultados fiables Resultados.
worker_connections: aumentar las conexiones de forma planificada
Elijo el conexiones_trabajadores Depende del tráfico de destino y de la combinación de protocolos; suele partir de 2048 o 4096. Para las API con mucho tráfico, considero la opción de 8192, siempre que los límites del sistema operativo y la memoria RAM lo permitan. Compruebo cada aumento con pruebas de carga, ya que las conexiones abiertas consumen memoria e influyen en el comportamiento del servidor de origen. Cuando predominan los handshakes SSL o las subidas de gran volumen, me baso más en los perfiles de CPU y E/S, y no solo en las cifras puras de conexiones. De este modo, me aseguro de que el número definido por trabajador Capacidad y que siga siendo realmente útil.
Sincronizar «worker_rlimit_nofile» y los límites del sistema operativo
Me encargo de que rlimit_nofile que cubra, como mínimo, la capacidad total teórica y que, a menudo, se configure con un margen de reserva. Para escenarios de proxy inverso, calculo un segundo descriptor por conexión de cliente hacia el servidor de origen. En consecuencia, suelo establecer rlimit_nofile al doble del número de conexiones simultáneas previstas. Aumento los límites del núcleo y del usuario (ulimit -n, fs.file-max) de tal forma que NGINX pueda realmente alcanzar esos valores. Si aparecen mensajes sobre archivos abiertos en el registro de errores, los aumento rápidamente y observo la Latencia de nuevo bajo carga.
Bloque de eventos: cómo utilizar eficazmente epoll y multi_accept
Activo en el bloque de eventos epoll y configuro „multi_accept“ en «on» para que los trabajadores acepten las conexiones en espera de una sola vez. Epoll reduce la sobrecarga cuando hay muchos sockets simultáneos y se adapta perfectamente al diseño no bloqueante de NGINX. Estos parámetros resultan muy útiles en picos de tráfico, ya que aceleran la fase de aceptación y permiten pasar más rápidamente al procesamiento propiamente dicho. En Linux, esta es mi configuración predeterminada, que solo modifico en contadas ocasiones especiales. Quien desee profundizar en el tema, puede comparar el modelo de bucle de eventos con Threadpool frente a bucle de eventos y de ahí deduce conclusiones para el propio entorno.
Afinidad de la CPU: asignar los trabajadores a los núcleos
He puesto afiliación de trabajadores a la CPU de forma específica cuando las cargas de trabajo son constantes y dependen de la CPU. Distribuyo el esquema de asignación mediante máscaras de bits para evitar cambios de contexto y favorecer la localidad de la caché. Con cuatro núcleos, asigno las máscaras de tal forma que cada trabajador disponga de su propio núcleo. A continuación, compruebo las tasas de fallos de caché, las latencias medianas y los percentiles del 99 % para observar claramente el efecto. Encontrarás explicaciones más detalladas sobre la afinidad y NUMA de forma concisa en Aplicación práctica de la afinidad de la CPU, lo cual resulta útil a la hora de ajustar con precisión Trabajador-Ayuda con los diseños.
Planificación de la capacidad: margen de seguridad y pruebas de carga
Cuando se trata de conexiones, tengo pensado hacer una Tampón que esté claramente por encima de los picos observados, para que los picos momentáneos no superen directamente los límites. Si duplico la carga máxima como punto de partida, dispongo de un margen de seguridad sólido en muchos escenarios. Si el tráfico fluctúa mucho, amplío aún más el margen de seguridad hasta que los percentiles del 99 se mantengan estables. A continuación, compruebo los cuellos de botella con herramientas como wrk o k6, observo las tasas de error y reviso las conexiones activas en el estado. Solo cuando las métricas son consistentes, aumento o reduzco de forma específica cada uno de los Valores.
Configuración y ejemplos de cálculo
Calculo la capacidad de conexión multiplicando el número de trabajadores por el número de conexiones por trabajador y, a partir de ahí, establezco unos límites más altos. Con cuatro núcleos de CPU, el modo «auto» y 4096 conexiones por trabajador, el cálculo me da un resultado de 16 384 conexiones simultáneas. En escenarios de proxy, suelo establecer rlimit_nofile en 32 768 o más, para que se incluyan los sockets de upstream. En el caso de máquinas pequeñas con dos núcleos, suelen bastar 2048 conexiones por trabajador, siempre que el porcentaje de subidas y de TLS se mantenga moderado. La siguiente tabla ayuda a clasificar los Valores iniciales:
| Núcleos de CPU | procesos_trabajadores | worker_connections (Inicio) | rlimit_nofile mínimo (valor orientativo) | Nota |
|---|---|---|---|---|
| 2 | auto (≈2) | 2048 | ≥ 4096 | Reserva Programar para TLS/proxy |
| 4 | auto (≈4) | 4096 | ≥ 16 384 | En el caso de los proxies, suele ser un factor de 2 en los FD |
| 8 | auto (≈8) | 4096-8192 | ≥ 32 768 | Prueba de carga decide sobre el aumento |
| 16+ | coche, o menos, si procede | 8192+ | ≥ 65 535 | Probar con afinidad y discreción |
NGINX Worker y Upstreams: cómo ponderar correctamente los escenarios
Distingo entre la entrega estática, el funcionamiento del proxy inverso y la carga de la pasarela de API, ya que son los Trabajador-Configuración diferente según el caso. Los contenidos estáticos consumen menos recursos, mientras que el TLS, la compresión y las conexiones upstream suponen una mayor carga para la CPU y los FD. Cuanto más grandes sean las claves SSL y más handshakes haya, más se beneficia la configuración „un worker por núcleo“. Las subidas de gran tamaño desplazan el perfil hacia la E/S, por lo que me centro más en rlimit_nofile y en los búferes de red. Cuando se producen tiempos de espera apreciables en la aceptación o en las respuestas del backend, me resulta útil tener una visión general de Colas y latencia, para evitar los puntos de estrangulamiento objetivo Resolver.
Flujo de trabajo práctico: paso a paso hacia un servidor más rápido
Empezaré por hacer un análisis de la situación actual de todos los aspectos relevantes Valores En el archivo nginx.conf, compruebo los núcleos de la CPU, el ulimit y los parámetros del kernel. A continuación, configuro worker_processes en «auto», establezco worker_connections, por ejemplo, en 4096 y aumento generosamente el valor de rlimit_nofile. En el bloque «events», activo epoll y multi_accept, y compruebo los registros con una recarga. A continuación, realizo pruebas de carga en condiciones reproducibles, en las que observo los tiempos de respuesta, las tasas de error y las conexiones abiertas. En el ajuste fino, siempre modifico solo una variable, documento cada paso y compruebo los efectos en los Métricas.
Entorno de alojamiento: recursos, kernel, red
Me aseguro de que haya suficiente CPU-Núcleos, suficiente RAM, unidades SSD o NVMe rápidas y un kernel de Linux actualizado. Solo así funcionan de forma fiable epoll, las pilas TCP modernas y las funciones de descarga de carga adecuadas. Ajusto los parámetros de red, como somaxconn y tcp_max_syn_backlog, al número deseado de conexiones para mantener cortas las colas de recepción. Un proveedor con un rendimiento de E/S sólido y una configuración del sistema de libre acceso resulta claramente ventajoso en este sentido. Las comparativas muestran que los servicios con Recursos Aumentar considerablemente los márgenes de NGINX.
Estrategia de keepalive: conexiones de cliente y de upstream
Utilizo Keepalive de forma deliberada como herramienta para gestionar la capacidad y la latencia. En el lado del cliente, configuro tiempo de espera de keepalive No demasiado alto, para que los sockets inactivos no se conexiones_trabajadores bloquear. Los valores entre 10 y 30 s suelen ofrecerme un buen equilibrio entre la reutilización y la ocupación de recursos. Con keepalive_requests Limito el número de solicitudes por conexión para cortar las conexiones prolongadas y evitar la sobrecarga de memoria. En el lado de origen (proxy inverso), mantengo conexiones persistentes con keepalive en el bloque «upstream», de modo que se prescinde de los handshakes y de la configuración de TCP. Para ello, ajusto el número por backend de forma conservadora en función de la capacidad del backend (max_conns), de lo contrario, yo mismo genero colas en el upstream. Importante: cada socket de keepalive cuenta como una conexión abierta y necesita FD; lo tengo en cuenta en rlimit_nofile y mi planificación del margen de maniobra.
Optimización de listas: reuseport, backlog y estrategia de aceptación
Distribuyo la carga de admisión de manera uniforme haciendo que SO_REUSEPORT activar (listen … reuseport). De este modo, cada worker dispone de su propia cola de aceptación, lo que reduce los „thundering herds“ y evita los puntos de congestión. En combinación con multi_accept acelero notablemente la fase de aceptación. La lista de...retraso (listen … backlog=) y sus equivalentes en el núcleo (somaxconn, tcp_max_syn_backlog) los configuro con un valor generoso para que los picos no se pierdan en la entrada del socket. La opción aplazado Aplaza la respuesta «Accept» hasta que se disponga de los datos; en el caso de muchas solicitudes de corta duración, esto puede resultar útil; en los demás casos, lo comparo en las pruebas. Si yo accept_mutex Lo aclaro en la prueba comparativa: con reuseport, suele ser prescindible; sin reuseport, puede mejorar la equidad, pero requiere coordinación. En este caso, tomo la decisión basándome en los datos, nunca por intuición.
Configurar los tiempos de espera y las colas de forma estable
He puesto Tiempos muertos de manera que los clientes lentos no saturen los trabajadores: tiempo de espera del encabezado del cliente y client_body_timeout Lo mantengo lo suficientemente conciso como para evitar atascos, pero lo suficientemente amplio para los usuarios reales. send_timeout evita que se bloqueen las respuestas dirigidas al cliente. En el contexto del proxy, defino proxy_connect_timeout, proxy_read_timeout y proxy_send_timeout de forma rigurosa, para que los backends que se cuelgan no paralicen el frontend. En el caso de backends con un paralelismo limitado, utilizo cola en el bloque «upstream» con un tiempo de espera, para amortiguar los picos y enviar el código de error 503 de forma controlada, en lugar de vincular a todos los trabajadores a los sockets de upstream en espera. Además, estabilizo el sistema con limit_req (Burst/Delay) y limit_conn rutas sensibles, de modo que los clientes o bots individuales no consuman recursos de forma desproporcionada.
Buffering, sendfile y AIO: elegir deliberadamente los métodos de E/S
He puesto sendfile para archivos estáticos y combínalo con tcp_nopush/tcp_nodelay dependiendo de la carga de trabajo, para agrupar paquetes de forma eficiente o reducir las latencias interactivas. Para archivos grandes, utilizo directio a partir de un umbral determinado, para evitar la contaminación de la caché y que no se sustituya la caché de páginas. En el modo proxy, decido si proxy_buffering ayuda (entrega rápida al cliente, lectura upstream desacoplada) o si, en caso de cargas de streaming, es mejor que proxy_request_buffering Reduzco para iniciar las subidas con antelación. Los tamaños de proxy_buffers, proxy_buffer_size y buffers_de_encabezado_de_cliente_grandes Lo controlo de forma deliberada para que el consumo de memoria por conexión no se dispare. Para que el acceso a los archivos no sobrecargue la CPU, estoy considerando aio (nativo o con subprocesos), pero haz pruebas exhaustivas, ya que las características del bucle de eventos y de E/S se influyen mutuamente.
HTTP/2, HTTP/3 y TLS: repercusiones en la capacidad de los trabajadores
Tengo en cuenta que HTTP/2 y HTTP/3 Modificar la dinámica de las conexiones: muchas solicitudes se ejecutan como Transmisiones a través de un número reducido de conexiones TCP o QUIC. Esto reduce el número de conexiones, pero aumenta los requisitos de CPU y memoria por conexión (multiplexación, compresión de encabezados, TLS/QUIC). Mi conexiones_trabajadores Por eso no lo interpreto a ciegas como „el mismo número de solicitudes“. Observo flujos concurrentes por conexión y pase tiempo de espera de keepalive y, si procede,. http2_max_concurrent_streams . Por el lado de TLS, salgo ganando con la reanudación de sesión (tickets/caché) y el OCSP stapling; así me ahorro costosos handshakes y mantengo bajas las latencias. La otra cara de la moneda: los keepalives más largos consumen FD y RAM, así que planeo rlimit_nofile y cuotas de memoria con reservas realistas. Para los algoritmos de cifrado que exigen un uso intensivo de la CPU, merece la pena probar la afinidad y la aceleración criptográfica moderna.
Visibilidad: estado, registros y métricas
Consigo transparencia con un enfoque ágil Estado-Endpoint (por ejemplo, stub_status), para ver las conexiones activas, los estados de lectura, escritura y espera, así como las solicitudes aceptadas. En los registros, procuro que haya poco ruido: un registro compacto log_format Con la hora, el estado, los tiempos de upstream y los bytes es suficiente para la mayoría de los análisis. Cuando el QPS es muy alto, desactivo el registro de acceso de forma selectiva (en función de la ubicación) o almaceno los registros en búfer de forma asíncrona, para que las operaciones de E/S no ralenticen el sistema. El registro de errores lo configuro en avise a o error y cambia temporalmente a depurar. De forma continua, correlaciono las latencias (mediana/percentil 95/99), las conexiones abiertas, las tasas de error del backend y la carga de la CPU por trabajador; a partir de ahí, deduzco los ajustes que hay que aplicar a las tres directivas clave y detecto a tiempo los efectos de saturación.
Contenedores y entornos virtuales: pasar los límites de forma clara
Compruebo en contenedores la cgroup-Establece los límites para la CPU, la RAM y los PID, y compáralos con la configuración de NGINX. ulimit -n debe ser lo suficientemente alto dentro del contenedor; de lo contrario, mis ajustes de rlimit_nofile no surtirán efecto. En el caso de las cuotas de CPU (por ejemplo, 2 vCPU), configuro procesos_trabajadores En consecuencia, para que la programación no se acelere de forma artificial. En lo que respecta a la red, en los modos de red „host“ me beneficio de una menor latencia de sobrecarga, mientras que las superposiciones suponen saltos adicionales. En hosts Multi-NUMA, presto atención a la afinidad y a los zócalos de memoria, para que los trabajadores no operen a través de distintos nodos. Lo mismo se aplica a la afinidad de IRQ y RPS/XPS: si las rutas desde la NIC, pasando por la IRQ, hasta el núcleo del trabajador son correctas, los picos de latencia se reducen de forma apreciable.
Ciclo de vida de una conexión: puertos efímeros, TIME_WAIT y reservas
Tengo pensado llevar suficiente Puertos efímeros (ip_local_port_range), cuando NGINX actúa como cliente activo frente a los servidores upstream. Cuando el rendimiento de las conexiones es muy alto, evito una fluctuación excesiva de los puertos mediante el „upstream-keepalive“, lo que reduce las colas TIME_WAIT. Utilizo con cautela los ajustes del kernel relacionados con la «reutilización»; las pilas modernas ya optimizan muchos aspectos internamente. Es más estable controlar la duración de las conexiones mediante valores adecuados de keepalive y timeout, y reuseport para garantizar una distribución equitativa. En el cálculo de la capacidad, además de los clientes, siempre tengo en cuenta el lado «upstream»; a menudo, son los FD los que constituyen el verdadero factor limitante, y no la «puerta de entrada».
Recargas y despliegues fluidos sin interrupciones
Utilizo el modelo maestro/esclavo para recargas fluidas: El master carga nuevas configuraciones; los antiguos workers se desconectan, mientras que los nuevos toman el relevo sin interrupciones. Con worker_shutdown_timeout Dejo que las solicitudes se completen correctamente sin bloquear recursos. Combino las implementaciones sin tiempo de inactividad en el servidor upstream con comprobaciones de estado y proxy_next_upstream-Establezco reglas para que los backends que fallan no aumenten la latencia global. Cuando realizo cambios en la configuración, siempre modifico solo un parámetro y compruebo los efectos en los registros y las métricas; así evito errores por confusión y mantengo la reproducibilidad del rendimiento.
Resumen conciso
Me acoplo procesos_trabajadores ajusto el número de núcleos (a ser posible, de forma automática), configuro `worker_connections` en función de la carga máxima y aumento generosamente `rlimit_nofile` junto con los límites del sistema operativo. En el bloque de eventos utilizo `epoll` y `multi_accept`, compruebo todo con pruebas de carga reproducibles y, a continuación, realizo ajustes graduales. Para las cargas de trabajo de proxy, tengo en cuenta descriptores adicionales y pruebo la afinidad de la CPU cuando las cargas de trabajo son constantes. Una pila bien configurada con un kernel adecuado, una E/S rápida y parámetros de red razonables marca la diferencia. Así es como consigo NGINX confiable en el ámbito de rendimiento que requieren las páginas y las API más exigentes.


