HTTP Keep-Alive Timeout: configuración óptima para el rendimiento del servidor

Con la atención puesta en Tiempo de espera HTTP Keep-Alive Le mostraré cómo establecer tiempos de inactividad para que las conexiones se reutilicen sin bloquear hilos. Explico valores específicos, muestro errores típicos y proporciono configuraciones probadas para nginx, Apache y el sistema operativo.

Puntos centrales

  • Saldo: Demasiado corto aumenta los apretones de manos, demasiado largo bloquea los hilos.
  • ValoresMayoritariamente 5-15 s y 100-500 peticiones por conexión.
  • CoordinaciónCoordinar los tiempos de espera de clientes, LB y cortafuegos.
  • Casos especialesWebSockets, SSE, sondeo largo por separado.
  • MonitoreoMonitoriza sockets abiertos, FDs y latencias.

HTTP Keep-Alive explicado brevemente

Mantengo conexiones TCP con Keep-Alive abierta para que varias peticiones utilicen la misma línea. Esto ahorra repetidos apretones de manos TCP y TLS y reduce la CPU-notablemente. Esto es especialmente beneficioso para muchos archivos pequeños como iconos, JSON o CSS. Cada nueva conexión que se evita reduce los cambios de contexto y alivia las rutinas del núcleo. En las pruebas con una alta proporción de GET, la duración total se reduce significativamente porque se generan menos paquetes SYN/ACK y fluye más tiempo de computación hacia la lógica de la aplicación.

Mido rápidamente el efecto: las latencias medias móviles se suavizan y el número de nuevas conexiones TCP por segundo desciende. No lo consigo por arte de magia, sino por Reutilización de conexiones y límites razonables. Sigue siendo importante que Keep-Alive no sustituya a un renderizado o almacenamiento en caché rápidos. Acorta los tiempos de espera en la frontera de la red, mientras que la propia aplicación debe seguir respondiendo con eficacia. Ambas cosas juntas aumentan el Actuación notablemente.

Comprender el tiempo de espera adecuado

El tiempo de espera define cuánto tiempo permanece abierta una conexión inactiva antes de que el servidor la cierre. cierra. Si lo pongo demasiado corto, los clientes abren constantemente nuevas conexiones TCP, lo que Sobrecarga se eleva. Si lo establezco demasiado largo, las conexiones ociosas aparcan trabajadores o hilos preciosos. El arte reside en el equilibrio entre la reutilización y el consumo de recursos. Hago pruebas prácticas: primero lo establezco de forma aproximada y luego lo afino con pruebas de carga.

También presto atención a la relación entre tiempos de respuesta y ventanas de inactividad. Si la interacción típica del usuario entre dos clics es de 2-4 segundos, un tiempo de espera de 5-15 segundos suele cubrir el patrón real. Las llamadas cortas a la API pueden tolerar fácilmente entre 5 y 10 segundos, y las cargas de trabajo multimedia, entre 10 y 15 segundos. Es importante no exagerar: los tiempos de espera excesivamente largos raramente conducen a más Rendimiento, pero a menudo conducen a bloqueos Recursos. Puedo reconocerlo rápidamente por el creciente número de enchufes abiertos y las elevadas cifras de FD.

Separar claramente los tipos de tiempo de espera

Hago una distinción estricta entre Tiempo de espera (Keep-Alive), Tiempo de espera de lectura/cabecera (el tiempo que el servidor espera las peticiones entrantes) y Tiempo de espera de envío/escritura (cuánto tiempo se tolera el envío hacia el cliente). Estas categorías cumplen tareas diferentes:

  • Tiempo de espera: Controla la reutilización y la duración del estacionamiento de las conexiones inactivas.
  • Tiempo de espera de lectura/cabecera: Protege contra clientes lentos (lorises lentos) y cabeceras enviadas a medias.
  • Tiempo de espera de envío/escritura: Evita que el servidor espere interminablemente por una recepción lenta en el cliente.

En nginx Utilizo deliberadamente header_timeout/read_timeout y send_timeout por contexto (http/servidor/ubicación) además de keepalive_timeout. Desde las versiones más recientes configuro opcionalmente keepalive_time, para limitar la duración máxima de una conexión, aunque siga activa. En Apache También utilizo RequestReadTimeout (mod_reqtimeout) y compruebe Tiempo de espera (global) separado de Tiempo de espera de KeepAlive. Esta separación es un elemento importante para evitar la inmovilización de recursos sin ningún beneficio real.

Valores recomendados en la práctica

Para entornos productivos, yo establezco un tiempo de espera de mantenimiento de la conexión de entre 5 y 15 segundos y entre 100 y 500 peticiones por conexión. Este intervalo consigue buenos índices de reutilización de conexiones y mantiene bajo el número de conexiones inactivas. En nginx Uso keepalive_timeout 10s como valor inicial y keepalive_requests 200. Si hay mucho tráfico, lo aumento moderadamente si veo demasiadas conexiones TCP nuevas. Si el tráfico es escaso, lo vuelvo a bajar para evitar una avalancha de tráfico ocioso.

Los que profundicen más se beneficiarán de un proceso de ajuste claro con puntos de medición. Para ello, resumo mis directrices en una guía práctica que describe el camino que va de la medición a la configuración y al control. Para empezar rápidamente, le remito a mis pasos en Ajuste de Keep Alive. Cómo controlar Reutilice y límites y evitar sorpresas. Al final, lo que cuenta es una latencia baja con Rendimiento.

Riesgos de los tiempos de espera prolongados

Un tiempo de espera largo mantiene las conexiones artificiales abra y bloquea a los trabajadores aunque no siga ninguna petición. Esto hace que los sockets se hinchen y se dispare el número de descriptores de fichero. Si el proceso alcanza sus límites, veo rechazar errores de aceptación o colas al establecer una conexión. La memoria crece, los recolectores de basura o asignadores cuestan tiempo adicional y la latencia aumenta. En caso de error, los clientes envían entonces a sockets que ya están cerrados y reciben crípticos Error.

Yo lo evito fijando valores moderados y comprobando regularmente las métricas. Si las conexiones inactivas aumentan demasiado con poca carga, reduzco el tiempo de espera. Si veo muchas conexiones nuevas por segundo durante los picos de tráfico, lo aumento cuidadosamente en pequeños pasos. Así mantengo el Capacidad utilizables y evitar las conexiones muertas. El resultado es un sistema más fluido con menos Consejos en las curvas.

Configuración: nginx, Apache y capa OS

Empiezo en el nivel del servidor web y establezco el tiempo de espera y los límites. En nginx Establezco keepalive_timeout 5-15s y keepalive_requests 100-500. En Apache con event-MPM combino KeepAlive On, KeepAliveTimeout 5-15 y MaxKeepAliveRequests 100-500. Luego calibro worker o thread pools según la carga esperada. Esto evita que los KeepAlive ociosos se conviertan en productivos. Tragamonedas encuadernar.

Aumento los límites y las colas a nivel del sistema operativo. Establezco ulimit -n en al menos 100.000, ajusto net.core.somaxconn y tcp_max_syn_backlog y compruebo el manejo de TIME_WAIT. Esto asegura que el kernel y el proceso tienen suficiente Recursos proporcionar. Por último, verifico las rutas desde la NIC mediante el equilibrio de IRQ hasta la aplicación. Esto me permite reconocer los cuellos de botella a tiempo y mantener el Latencia bajo.

Componente Directiva/Establecimiento Recomendación Nota
nginx tiempo de espera de keepalive 5-15 s Más corto con poco tráfico, más largo con muchas peticiones pequeñas
nginx keepalive_requests 100-500 Recicla compuestos y reduce Fugas
Apache (evento) Tiempo de espera de KeepAlive 5-15 s Event-MPM gestiona el ralentí de forma más eficiente que prefork
Sistema operativo ulimit -n ≥ 100.000 Más FD abiertos para muchos Enchufes
Sistema operativo net.core.somaxconn Aumentar Menos conexiones rechazadas en Carga máxima

Proxy inverso y reutilización ascendente

Siempre pienso en mantener vivo de extremo a extremo. Detrás del servidor edge suele haber una cadena de proxy inverso → servidores de aplicaciones. Para nginx, activo mi propio Piscinas Keep Alive (upstream keepalive, keepalive_requests, keepalive_timeout), establecer proxy_http_version 1.1 y eliminar „Connection: close“. Esto también me ahorra interno y descargo los backends de las aplicaciones (Node.js, Java, PHP-FPM). En Apache con mod_proxy, también mantengo conexiones persistentes a servidores backend y las limito por destino para que un hotspot no monopolice los pools.

Mido por separado: Tasa de reutilización Client→Edge y Edge→Backend. Si veo una buena reutilización en el borde, pero muchas conexiones nuevas al backend, aumento selectivamente los pools de subida. Esto me permite escalar sin aumentar globalmente los tiempos de espera del frontend.

Trabajadores, hilos y límites del sistema operativo

No dimensiono trabajadores, eventos e hilos según valores deseados, sino según perfil de carga. Para ello, controlo las peticiones activas, los trabajadores inactivos, la utilización del bucle de eventos y los cambios de contexto. Si los hilos están aparcados en modo inactivo, reduzco el tiempo de espera o los límites máximos de hilos inactivos. Si veo un 100% de CPU todo el tiempo, compruebo las colas de aceptación, la distribución de IRQ y la pila de red. Pequeñas correcciones en los límites de FD y en las acumulaciones suelen suponer una gran diferencia. Efectos.

Planifico el margen de forma realista. Una reserva del 20-30% en hilos y FD proporciona seguridad para los picos. Si me excedo, pierdo cachés y aumentan los residuos. Si me paso, las peticiones acaban en las colas o expiran. La intersección correcta de Capacidad y eficiencia mantiene bajas las latencias y protege el Estabilidad.

Coordinar los tiempos de espera del cliente, el equilibrador de carga y el cortafuegos.

Establezco límites de tiempo a lo largo de todo el camino para que no haya callejones sin salida. Conexiones se crean. Lo ideal es que los clientes cierren mínimamente antes que el servidor. El equilibrador de carga no debe cortar más corto, de lo contrario veré reinicios inesperados. Incluyo valores de inactividad de NAT y cortafuegos para que las conexiones no se pierdan en la ruta de la red. desaparecer. Este ajuste evita las retransmisiones y suaviza las curvas de carga.

Mantengo la cadena comprensible con diagramas claros: Cliente → LB → servidor web → app. Documento los tiempos de espera en reposo, los tiempos de espera de lectura/escritura y las estrategias de reintento para cada enlace. Si cambio un valor, compruebo los vecinos. Así mantengo la coherencia de la ruta y obtengo resultados de medición reproducibles. Esta disciplina ahorra tiempo en la Solución de problemas y aumenta la fiabilidad.

Seguridad: Protección contra los loros lentos y los abusos ociosos

Tiempos muertos demasiado generosos Superficies de ataque. Por lo tanto, establezco límites que permiten la reutilización legítima pero hacen más difícil mantenerlos abiertos maliciosamente. En nginx, header y read_timeout, request_headers_size limits y un límite superior duro para keepalive_requests ayudan. En Apache, uso mod_reqtimeout y limito las conexiones paralelas por IP. Límites de velocidad y limit_conn en nginx también protegen contra inundaciones de muchos sockets ociosos. Para los endpoints de larga ejecución, separo pools dedicados para que los ataques a los flujos no comprometan a los trabajadores regulares de la API.

Casos especiales: Long Polling, SSE y WebSockets

Las corrientes largas chocan con las cortas Tiempos muertos y necesitan sus propias reglas. Técnicamente, separo estos puntos finales de las rutas clásicas de API y activos. Para SSE y WebSockets, establezco tiempos de espera más altos, grupos de trabajadores dedicados y límites estrictos por IP. Mantengo viva la conexión con heartbeats o ping/pong y reconozco rápidamente las desconexiones. De esta forma, los flujos no bloquean los hilos para las conexiones regulares. Peticiones cortas.

Limito las conexiones simultáneas y mido activamente. Los límites demasiado altos consumen FD y RAM. Los límites demasiado estrechos cortan el acceso a los usuarios legítimos. Encuentro el punto óptimo con métricas limpias para conexiones abiertas, inactivas, activas y perdidas. Esta separación me ahorra Aumenta los tiempos muertos y protege el Capacidad.

HTTP/2, multiplexación y keep-alive

HTTP/2 multiplexa varios flujos a través de un Conexión, pero sigue dependiendo de los tiempos de espera. Mantengo la ventana de inactividad moderada porque las sesiones también pueden aparcarse bajo HTTP/2. Los keepalive_requests altos son menos importantes aquí, pero el reciclaje sigue siendo útil. El bloqueo de cabeza de línea se traslada al nivel de trama, por lo que sigo midiendo la latencia por Corriente. Si desea comparar con más detalle, encontrará información de fondo en Multiplexación HTTP/2.

En HTTP/2, presto especial atención al número de flujos activos por conexión. Demasiados flujos paralelos pueden sobrecargar los hilos de la aplicación. Entonces reduzco los límites o aumento los trabajadores del servidor. Lo mismo se aplica aquí: medir, ajustar, medir de nuevo. Esto mantiene el Tiempos de respuesta escasos y conservados Recursos.

TLS, reanudación de sesión y HTTP/3/QUIC

Los apretones de manos TLS son caros. Yo uso Reanudación de la sesión (tickets/IDs) y grapado OCSP para que las reconexiones sean más rápidas en caso de que finalice una conexión. En HTTP/3, QUIC se hace cargo de la capa de transporte: aquí el Tiempo de espera QUIC similar a Keep-Alive, pero sobre una base UDP. En este caso, también mantengo las ventanas moderadas y mido las retransmisiones, ya que las pérdidas de paquetes tienen un efecto diferente que con TCP. Para entornos mixtos (H1/H2/H3), selecciono valores de referencia normalizados y hago ajustes finos para cada protocolo.

Supervisión, métricas y pruebas de carga

Confío más en los datos de las mediciones que en mis corazonadas y empiezo por tener las cosas claras. Indicadores clave de rendimiento. Son importantes: sockets abiertos, utilización de FD, nuevas conexiones/s, latencias (P50/P90/P99), tasas de error y retransmisiones. Ejecuto perfiles de carga realistas: Calentamiento, meseta, rampa de descenso. Luego comparo las curvas antes y después de modificar el tiempo de espera. Un vistazo a Cola de servidores ayuda a interpretar claramente los tiempos de espera.

Documento cada ajuste con un sello de tiempo y los valores medidos. Esto me permite conservar el historial y reconocer las correlaciones. Me tomo en serio los efectos negativos y los revierto rápidamente. Los pasos pequeños y comprensibles ahorran mucho tiempo. Al final, lo que cuenta es la estabilidad. Latencia y baja Tasa de error bajo carga.

Métodos y herramientas de medición en la práctica

  • Pruebas rápidas: Utilizo herramientas como wrk, ab o vegeta para comprobar las cuotas de reutilización (-H connection: keep-alive vs. close), las conexiones/s y los percentiles de latencia.
  • Vista del sistema: ss/netstat muestra estados (ESTABLISHED, TIME_WAIT), lsof -p el consumo de FD, dmesg/syslog indicaciones de caídas.
  • Métricas del servidor web: nginx stub_status/VTS y Apache mod_status proporcionan activo/ocupado/en espera y peticiones/s. A partir de esto puedo reconocer picos de inactividad o cuellos de botella de los trabajadores.
  • Rastros: Utilizo el rastreo distribuido para controlar si los tiempos de espera se producen en el límite de la red o en la aplicación.

Configurar paso a paso

En primer lugar, determino el patrón de uso real: cuántas peticiones por sesión, qué Intervalos entre clics, cómo de grandes son las respuestas. A continuación, establezco un perfil inicial: timeout 10 s, keepalive_requests 200, número moderado de trabajadores. A continuación, realizo pruebas de carga con datos representativos. Evalúo el número de nuevas conexiones por segundo y la utilización del FD. A continuación, ajusto el Valores en incrementos de 2-3 segundos.

Repito el ciclo hasta que las latencias se mantienen estables bajo carga y los picos de FD no alcanzan el límite. Con mucho tráfico, sólo aumento el tiempo de espera si veo claramente menos conexiones nuevas y los trabajadores siguen libres. Con una utilización baja, reduzco el tiempo de espera para evitar la inactividad. En casos especiales como SSE, establezco bloques de servidores dedicados con límites más altos. Este camino conduce a un Configuración sin cartón de tarifa.

Kubernetes, contenedores y autoescalado

En entornos de contenedores utilizo conntrack-límites, límites de pod FD y backlogs de nodos. Garantizo tiempos de espera coherentes entre Ingress, la malla de servicios/proxy y la aplicación. Para el autoescalado, presto atención a Tiempos de drenajeCuando se terminan los pods, deberían rechazar nuevas conexiones mediante „Connection: close“ y servir las existentes limpiamente. Los valores Keep alive demasiado largos alargan los drenajes innecesariamente, mientras que los demasiado cortos generan tormentas de handshake al escalar.

Apagado gradual y despliegues continuos

También tengo previsto desconectar. Antes de un despliegue, reduzco gradualmente Keep-Alive o envío dirigidos Conexión: cerrar en Respuestas para que los clientes no abran nuevas conexiones inactivas. En nginx, un worker_shutdown_timeout para las peticiones en ejecución. En Apache, utilizo mecanismos de gracia y vigilo MaxConnectionsPerChild/Worker para que el reciclaje se realice automáticamente con el tiempo. Esto mantiene los despliegues sin problemas sin tener que limitar los sockets abiertos.

Ajuste del sistema operativo: puertos, tiempos de espera, parámetros del kernel

  • puertos efímeros: Seleccione un rango ip_local_port_amplio para que las conexiones de corta duración no sufran escasez.
  • TIME_WAIT: Vigilo los picos de TW. Las pilas modernas lo manejan bien; yo evito los picos dudosos (tw_recycle).
  • tcp_keepalive_time: No lo estoy confundiendo con HTTP Keep-Alive. Es un mecanismo del kernel para reconocer pares muertos - útil detrás de NAT, pero no un sustituto de la ventana de inactividad HTTP.
  • Atrasos y topes: dimensiona somaxconn, tcp_max_syn_backlog y rmem/wmem de forma sensata para no estrangular bajo carga.

Lista de comprobación para la resolución de problemas

  • Muchas conexiones nuevas a pesar de keep-alive: Tiempo de espera demasiado corto o los clientes/LB se cortan antes.
  • Altas cifras de ralentí y FDs completos: Tiempo de espera demasiado largo o grupos de trabajadores demasiado grandes para el patrón de tráfico.
  • Error RST/Timeout para sesiones largas: NAT/firewall inactivo demasiado corto en la ruta, asimetría entre enlaces.
  • Latencias de cola larga (P99): Compruebe los tiempos de espera de envío y lectura, la lentitud de los clientes o el exceso de trabajo acumulado.
  • Backends sobrecargados a pesar de la baja carga en los bordes: Falta jaula aguas arriba o es demasiado pequeña.

Perfiles de prácticas y valores iniciales

  • API-first (llamadas cortas): Keep-Alive 5-10 s, keepalive_requests 200-300, tight header/read timeouts.
  • Comercio electrónico (mixto): 8-12 s, 200-400, un poco más generoso para imágenes de productos y visitas en caché.
  • Activos/CDN-like (muchos archivos pequeños): 10-15 s, 300-500, fuertes pozas río arriba y altos límites de FD.
  • Intranet/baja carga: 5-8 s, 100-200, para que no domine el ralentí.

Brevemente resumido

Configuro el tiempo de espera HTTP keep-alive para que las conexiones se reutilicen sin bloquear hilos. En la práctica, 5-15 segundos y 100-500 peticiones por conexión dan muy buenos resultados. Coordino los tiempos de espera del cliente, el equilibrador de carga y el cortafuegos, separo las conexiones de larga duración como WebSockets y regulo los límites del sistema operativo. Con una monitorización limpia, pruebas de carga realistas y pequeños pasos, consigo un bajo Latencias y alta Rendimiento. Quienes mantienen esta disciplina obtienen un rendimiento cuantificable del hardware existente.

Artículos de actualidad