...

HTTP Keep-Alive Timeout : configuration optimale pour les performances du serveur

En mettant l'accent sur HTTP Keep-Alive Timeout je te montre comment régler les temps d'inactivité de manière à ce que les connexions soient réutilisées sans bloquer les threads. J'explique des valeurs concrètes, je montre des pièges typiques et je fournis des configurations éprouvées pour nginx, Apache et le système d'exploitation.

Points centraux

  • Balance: trop court augmente les poignées de main, trop long bloque les threads.
  • Valeurs: Généralement 5-15 s et 100-500 requêtes par connexion.
  • Coordination: coordonner les timeouts du client, du LB et du pare-feu.
  • Cas spéciaux: traiter séparément les WebSockets, SSE, Long Polling.
  • Suivi: Observer les sockets ouverts, les FD et les latences.

HTTP Keep-Alive en bref

Je maintiens les connexions TCP avec Keep-Alive ouvert pour que plusieurs requêtes utilisent la même ligne. Cela me permet d'économiser des handshakes TCP et TLS répétés et de réduire la charge de travail. CPU-Overhead se fait sentir. Cela s'avère particulièrement utile pour les nombreux petits fichiers tels que les icônes, JSON ou CSS. Chaque nouvelle connexion évitée réduit les changements de contexte et allège les routines du noyau. Dans les benchmarks avec une forte proportion de GET, la durée totale diminue nettement, car il y a moins de paquets SYN/ACK et plus de temps de calcul est consacré à la logique de l'application.

Je mesure rapidement l'effet : les latences moyennes flottantes deviennent plus lisses et le nombre de nouvelles connexions TCP par seconde diminue. Je n'obtiens pas cela par magie, mais grâce à Recours à la connexion et des limites raisonnables. Il est important de noter que Keep-Alive ne remplace pas un rendu rapide ou une mise en cache. Il réduit les temps d'attente à la frontière du réseau, tandis que l'application elle-même doit continuer à répondre efficacement. Ces deux éléments combinés mettent en valeur la Performance de manière perceptible.

Comprendre le bon délai d'attente

Le délai d'attente détermine la durée pendant laquelle une connexion inactive reste ouverte avant que le serveur ne la ferme. ferme. Si je le fais trop court, les clients ouvrent constamment de nouvelles connexions TCP, ce qui Overhead de l'ordinateur. Si je le fixe à une durée trop longue, les connexions en mode veille parquent des travailleurs ou des threads précieux. Tout l'art consiste à trouver un équilibre entre la réutilisation et la consommation de ressources. Je teste de manière pratique : d'abord un réglage approximatif, puis un ajustement fin à l'aide de tests de charge.

Je fais en outre attention au rapport entre les temps de réponse et les fenêtres d'inactivité. Si l'interaction typique de l'utilisateur entre deux clics est de 2 à 4 secondes, 5 à 15 secondes de délai d'attente couvrent généralement le modèle réel. Les appels API courts supportent facilement 5-10 secondes, les charges de travail médias plutôt 10-15 secondes. Il est important de noter que je n'exagère pas : les timeouts trop longs entraînent rarement une augmentation des performances. Débit, mais qui entraînent souvent des blocages Ressources. Je le constate rapidement à l'augmentation du nombre de sockets ouverts et du nombre de FD.

Séparer proprement les types de timeout

Je fais une distinction stricte entre Délai d'attente en mode veille (Keep-Alive), Délai de lecture/d'en-tête (combien de temps le serveur attend les requêtes entrantes) et Timeout d'envoi/d'écriture (combien de temps l'envoi en direction du client est toléré). Ces catégories remplissent des fonctions différentes :

  • Délai d'attente en mode veille : Contrôle la réutilisation et la durée de stationnement des connexions inactives.
  • Délai d'attente de lecture/en-tête : Protège contre les clients lents (slowloris) et les en-têtes à moitié envoyés.
  • Timeout d'envoi/d'écriture : Empêche le serveur d'attendre indéfiniment une réception lente chez le client.

À l'adresse suivante : nginx j'utilise délibérément, en plus de keepalive_timeout, header_timeout/read_timeout et send_timeout par contexte (http/server/localisation). Depuis les versions récentes, j'utilise en option keepalive_time, pour plafonner la durée de vie maximale d'une connexion, même si elle reste active. Dans Apache j'utilise en complément RequestReadTimeout (mod_reqtimeout) et vérifie Délai d'attente (global) séparé de KeepAliveTimeout. Cette séparation est un élément important contre l'engagement de ressources sans utilité réelle.

Valeurs recommandées dans la pratique

Pour les environnements productifs, je fixe un délai d'attente de 5 à 15 secondes et 100 à 500 requêtes par connexion. Cette fourchette permet d'atteindre de bons taux d'utilisation des connexions et de limiter le nombre de connexions dormantes. Sur nginx j'utilise keepalive_timeout 10s comme valeur de départ et keepalive_requests 200. En cas de trafic très important, j'augmente modérément si je vois trop de nouvelles connexions TCP. Si le trafic est faible, je diminue à nouveau afin d'éviter une vague d'inactivité.

Ceux qui vont plus loin profitent d'un processus de réglage clair avec des points de mesure. Pour ce faire, je résume mes lignes directrices dans un guide pratique qui décrit le parcours de la mesure au contrôle en passant par la configuration. Pour un démarrage rapide, je renvoie à mes étapes dans Réglage Keep Alive. Comment contrôler Réutilisation et les limites et évite les surprises. En fin de compte, ce qui compte, c'est une faible latence et une stabilité des données. Débit.

Risques de temps morts trop longs

Un long timeout maintient artificiellement les connexions ouvert et bloque les travailleurs bien qu'aucune requête ne suive. Cela fait gonfler les sockets et fait grimper le nombre de descripteurs de fichiers. Lorsque le processus atteint ses limites, je vois des erreurs d'acceptation de rejet ou des files d'attente lors de l'établissement de la connexion. La mémoire augmente, le garbage collector ou l'allocator coûtent du temps supplémentaire et la latence augmente. En cas d'erreur, les clients envoient des messages sur des sockets déjà fermées et reçoivent des messages cryptés. Erreur.

J'évite cela en fixant des valeurs modérées et en vérifiant régulièrement les métriques. Si les connexions en mode veille augmentent trop en cas de faible charge, j'abaisse le délai d'attente. Si je vois beaucoup de nouvelles connexions par seconde pendant les pics de trafic, je l'augmente prudemment par petites étapes. C'est ainsi que je maintiens la Capacité et évite les connexions mortes. Le résultat est un système plus calme avec moins de Pointes dans les virages.

Configuration : nginx, Apache et couche OS

Je commence au niveau du serveur web et je définis le délai d'attente et les limites. Sur nginx je règle keepalive_timeout 5-15s et keepalive_requests 100-500. Dans Apache avec event-MPM, je combine KeepAlive On, KeepAliveTimeout 5-15 et MaxKeepAliveRequests 100-500. Ensuite, je calibre les pools de travailleurs ou de threads en fonction de la charge attendue. Cela évite que les alives de mise en veille ne deviennent productifs. Slots lier.

Au niveau du système d'exploitation, j'augmente les limites et les files d'attente. Je fixe ulimit -n à au moins 100.000, j'ajuste net.core.somaxconn et tcp_max_syn_backlog et je vérifie la gestion de TIME_WAIT. Je m'assure ainsi que le noyau et le processus ont suffisamment de Ressources pour les mettre à disposition. Pour finir, je vérifie les chemins du NIC à l'app en passant par l'équilibrage des IRQ. Cela me permet de repérer à temps les goulets d'étranglement et de maintenir les Latence bas.

Composant Directive/Setting Recommandation Remarque
nginx keepalive_timeout 5 à 15 s Plus court en cas de faible trafic, plus long en cas de nombreuses petites requêtes
nginx keepalive_requests 100–500 Recycle les composés et réduit Leaks
Apache (événement) KeepAliveTimeout 5 à 15 s Event-MPM gère Idle plus efficacement que prefork
Système d'exploitation ulimit -n ≥ 100.000 Plus de DA ouverts pour beaucoup Prises
Système d'exploitation net.core.somaxconn Augmenter Moins de connexions refusées sous Charge de pointe

Proxy inverse et réutilisation en amont

Je pense Keep-Alive toujours de bout en bout. Derrière le serveur Edge se trouve souvent une chaîne de serveurs d'applications Reverse Proxy →. Pour nginx, j'active sur la page amont mes propres Piscines Keep-Alive (upstream keepalive, keepalive_requests, keepalive_timeout), définir proxy_http_version 1.1 et supprimer „Connection : close“. Ainsi, j'économise aussi interne handshake et déleste les backends d'apps (Node.js, Java, PHP-FPM). Dans Apache avec mod_proxy, je garde également des connexions persistantes vers les serveurs backend et je les limite par destination afin qu'un hotspot ne monopolise pas les pools.

Je mesure séparément : le taux de réutilisation Client→Edge et Edge→Backend. Si je vois une bonne réutilisation à la périphérie, mais beaucoup de nouvelles connexions au backend, j'augmente sélectivement les pools en amont. Cela me permet de passer à l'échelle sans augmenter globalement les délais d'attente du front-end.

Travailleurs, threads et limites du système d'exploitation

Je ne dimensionne pas les workers, les événements et les threads en fonction de valeurs souhaitées, mais en fonction de profil de charge. Pour cela, j'observe les requêtes actives, les travailleurs au repos, l'utilisation des boucles d'événements et les changements de contexte. Si des threads sont parqués au ralenti, j'abaisse le délai d'attente ou les limites maximales de ralenti par thread. Si je vois un CPU à 100 % en permanence, je vérifie les queues d'acceptation, la répartition des IRQ et la pile réseau. De petites corrections des limites FD et des backlogs apportent souvent de grands avantages. Effets.

Je prévois une marge de manœuvre réaliste. Une réserve de 20 à 30 % dans les threads et les FD me permet de faire face aux pics. Si j'exagère, je perds des caches et le gaspillage augmente. Si j'exagère, les requêtes finissent dans des files d'attente ou expirent. Le bon équilibre entre Capacité et l'efficacité maintient les latences à un niveau bas et protège les Stabilité.

Coordonner les délais d'attente des clients, des équilibreurs de charge et des pare-feu

Je fixe des limites de temps tout au long du parcours pour éviter les morts. Connexions se produisent. Les clients ferment idéalement un peu plus tôt que le serveur. L'équilibreur de charge ne doit pas couper plus court, sinon je vois des réinitialisations inattendues. J'intègre les valeurs NAT et Firewall Idle, afin que les connexions ne se fassent pas dans le chemin du réseau. disparaissent. Ce réglage permet d'éviter les retransmissions et de lisser les courbes de charge.

Avec des diagrammes clairs, je garde la chaîne compréhensible : client → LB → serveur web → app. Pour chaque maillon, je documente les timeouts d'inactivité, les timeouts de lecture/écriture et les stratégies de retry. Si je modifie une valeur, je vérifie les valeurs voisines. Ainsi, le chemin reste cohérent et j'obtiens des résultats de mesure reproductibles. Cette discipline permet de gagner du temps lors de la Dépannage et augmente la Fiabilité.

Sécurité : protection contre les slowloris et l'abus d'idle

Ouvrir des timeouts trop généreux Surfaces d'attaque. Je fixe donc des limites qui permettent une réutilisation légitime, mais qui rendent difficile l'ouverture malveillante. Dans nginx, les limites header et read_timeout, request_headers_size et une limite supérieure stricte pour keepalive_requests sont utiles. Dans Apache, j'utilise mod_reqtimeout et je limite les connexions parallèles par IP. Limites de débit et limit_conn dans nginx protègent en outre contre les inondations de nombreuses sockets inoccupées. Pour les points de terminaison à longue distance, je sépare des pools dédiés afin que les attaques sur les flux ne lient pas les API-workers réguliers.

Cas particuliers : long polling, SSE et WebSockets

Les flux longs entrent en collision avec les flux courts Timeouts et ont besoin de leurs propres règles. Je sépare techniquement ces points finaux des routes API et des routes d'actifs classiques. Pour SSE et WebSockets, je fixe des délais d'attente plus élevés, des pools de travailleurs dédiés et des limites strictes par IP. Avec Heartbeats ou Ping/Pong, je garde la connexion vivante et je détecte rapidement les interruptions. Ainsi, les flux ne bloquent pas les threads pour les connexions régulières. Requêtes courtes.

Je limite les connexions simultanées et je les mesure activement. Les limites trop élevées consomment des FD et de la mémoire. Des limites trop étroites coupent les utilisateurs légitimes. Avec des métriques propres sur les connexions ouvertes, inactives, actives et interrompues, je trouve le "sweet spot". Cette séparation m'évite d'avoir à gérer des Augmentations des timeouts et protège les Capacité.

HTTP/2, multiplexage et Keep-Alive

HTTP/2 multiplexe plusieurs flux via une Connexion, mais reste tributaire des délais d'attente. Je maintiens la fenêtre d'inactivité à un niveau modéré, car des sessions peuvent aussi être parquées sous HTTP/2. Les keepalive_requests élevés sont moins importants ici, mais un recyclage reste utile. Le head-of-line blocking se déplace au niveau des trames, je continue donc à mesurer la latence par Flux. Ceux qui souhaitent approfondir la comparaison trouveront des informations de fond sur Multiplexage HTTP/2.

Sous HTTP/2, je surveille particulièrement le nombre de flux actifs par connexion. Un trop grand nombre de flux parallèles peut surcharger les fils d'application. Je freine alors les limites ou augmente le nombre de serveurs. Ici aussi, la règle est la suivante : mesurer, ajuster, mesurer à nouveau. Cela permet de maintenir la Temps de réponse juste et préservé Ressources.

TLS, résumés de session et HTTP/3/QUIC

Les handshakes TLS sont chers. Je mets Résomption de session (tickets/IDs) et OCSP-Stapling, afin que les reconnexions soient plus rapides si une connexion se termine malgré tout. Sous HTTP/3, QUIC prend en charge la couche de transport : ici, l'attribut QUIC idle timeout similaire à Keep-Alive, mais basé sur UDP. Là aussi, je maintiens les fenêtres à un niveau modéré et je mesure les retransmissions, car les pertes de paquets ont un effet différent de celui du TCP. Pour les environnements mixtes (H1/H2/H3), je choisis des valeurs indicatives uniformes et j'ajuste finement par protocole.

Monitoring, métriques et tests de charge

Je fais plus confiance aux données de mesure qu'à mon instinct et je commence par des objectifs clairs. KPIs. Les éléments importants sont : les sockets ouverts, l'utilisation de FD, les nouvelles connexions/s, les latences (P50/P90/P99), les taux d'erreur et les retransmissions. Je conduis des profils de charge réalistes : Warmup, Plateau, Ramp-down. Ensuite, je compare les courbes avant et après les modifications du timeout. Un coup d'œil sur Mise en file d'attente du serveur aide à interpréter proprement les temps d'attente.

Je documente chaque adaptation avec un horodatage et des valeurs de mesure. Je conserve ainsi l'historique et j'identifie les corrélations. Je prends au sérieux les effets négatifs et je les atténue rapidement. Les petites étapes compréhensibles font gagner beaucoup de temps. Au final, ce qui compte, c'est la stabilité Latence et faible Taux d'erreur en charge.

Méthodes et outils de mesure dans la pratique

  • Tests rapides : Avec des outils comme wrk, ab ou vegeta, je vérifie les taux de reuse (-H Connection : keep-alive vs. close), les connexions/s et les percentiles de latence.
  • Vue du système : ss/netstat indiquent des états (ESTABLISHED, TIME_WAIT), lsof -p la consommation de FD, dmesg/syslog des indications sur les drops.
  • Métriques du serveur web : nginx stub_status/VTS et Apache mod_status fournissent Active/Idle/Waiting et Requests/s. Cela me permet de détecter les pics d'inactivité ou les goulots d'étranglement des travailleurs.
  • Traces : Avec le traçage distribué, j'observe s'il y a des temps d'attente à la frontière du réseau ou dans l'application.

Configurer pas à pas

Tout d'abord, je détermine le modèle d'utilisation réel : combien de requêtes par session, quelles Intervalles entre les clics, quelle est la taille des réponses. Ensuite, je définis un profil initial : timeout 10 s, keepalive_requests 200, nombre de worker modéré. Ensuite, je réalise des tests de charge avec des données représentatives. J'évalue le nombre de nouvelles connexions par seconde et l'occupation FD. Ensuite, j'ajuste les Valeurs par paliers de 2 à 3 secondes.

Je répète le cycle jusqu'à ce que les latences restent stables sous la charge et que les pics de FD n'atteignent pas la limite. En cas de fort trafic, je n'augmente le timeout que si je vois clairement moins de nouvelles connexions et que les worker restent malgré tout libres. En cas de faible charge, je réduis le timeout afin d'éviter les temps morts. Dans des cas particuliers comme SSE, je place des blocs de serveurs dédiés avec des limites plus élevées. Ce chemin mène à un système Réglage sans carton à deviner.

Kubernetes, conteneurs et auto-scaling

Dans les environnements de conteneurs, j'obtiens conntrack-Limites de pod FD et backlogs de nœuds. Je veille à ce que les délais d'attente soient cohérents entre Ingress, le service mesh/proxy et l'application. Pour l'auto-scaling, je veille à Temps de drainageLorsque les pods se terminent, ils doivent refuser les nouvelles connexions par „Connection : close“ et servir proprement les connexions existantes. Des valeurs Keep-Alive trop longues prolongent inutilement les drains, des valeurs trop courtes génèrent des tempêtes de handshake lors du scale-out.

Arrêt progressif et déploiements roulants

Je prévois la déconnexion. Avant un déploiement, je réduis progressivement Keep-Alive ou j'envoie de façon ciblée Connexion : fermée sur Responses, afin que les clients n'ouvrent pas de nouvelles connexions Idle. Dans nginx, une worker_shutdown_timeout pour les requêtes en cours. Dans Apache, j'utilise des mécanismes Graceful et je garde un œil sur MaxConnectionsPerChild/Worker pour que le recyclage se fasse automatiquement au fil du temps. Ainsi, les déploiements restent fluides, sans couper brutalement les sockets ouverts.

OS-Tuning : ports, timeouts, paramètres du noyau

  • ports éphémères : Choisir ip_local_port_range large, afin que les connexions de courte durée ne fonctionnent pas en pénurie.
  • TIME_WAIT : Je surveille les pics de tw. Les piles modernes gèrent bien cela ; j'évite les tweaks douteux (tw_recycle).
  • tcp_keepalive_time : Je ne le confonds pas avec HTTP Keep-Alive. Il s'agit d'un mécanisme du noyau pour détecter les pairs morts - utile derrière le NAT, mais qui ne remplace pas la fenêtre HTTP Idle.
  • Backlogs et buffers : dimensionner judicieusement somaxconn, tcp_max_syn_backlog et rmem/wmem afin de ne pas étrangler en charge.

Liste de contrôle de dépannage

  • Beaucoup de nouveaux liens/s malgré Keep-Alive : Timeout trop court ou les clients/LB coupent plus tôt.
  • Chiffres Idle élevés et FDs pleins : Timeout trop long ou pools de travailleurs trop importants pour le modèle de trafic.
  • Erreur de RST/timeout lors de sessions prolongées : NAT/Firewall-Idle trop court dans le chemin, asymétrie entre les maillons.
  • Longues latences de queue (P99) : Vérifier les timeouts d'envoi/de lecture, les clients lents ou les backlogs surchargés.
  • Backends surchargés malgré une charge Edge faible : Rejet en amont absent ou sous-dimensionné.

Profils pratiques et valeurs de départ

  • API-first (appels courts) : Keep-Alive 5-10 s, keepalive_requests 200-300, header/read timeouts serrés.
  • Commerce électronique (mixte) : 8-12 s, 200-400, légèrement plus généreux pour les images de produits et les hits de cache.
  • Assets/similaire à un CDN (nombreux petits fichiers) : 10-15 s, 300-500, des pools amont forts et des limites FD élevées.
  • Intranet/faible charge : 5-8 s, 100-200, pour que Idle ne domine pas.

En bref

Je règle le délai d'attente HTTP Keep-Alive de manière à ce que les connexions soient réutilisées sans bloquer les threads. En pratique, 5-15 secondes et 100-500 requêtes par connexion donnent de très bons résultats. Je coordonne les timeouts des clients, des load balancers et des firewalls, je sépare les longues distances comme les WebSockets et je régule les limites OS. Avec un monitoring propre, des tests de charge réalistes et des petits pas, je parviens à obtenir des taux d'occupation bas. Latence et de haute Débit. Celui qui respecte cette discipline obtient des performances mesurables à partir du matériel existant.

Derniers articles