http keep-alive à l'ère moderne

92

Donc, selon l'auteur haproxy, qui en sait une ou deux sur http:

Keep-alive a été inventé pour réduire l'utilisation du processeur sur les serveurs lorsque les processeurs étaient 100 fois plus lents. Mais ce qui n'est pas dit, c'est que les connexions persistantes consomment beaucoup de mémoire tout en n'étant utilisables par personne sauf le client qui les a ouvertes. Aujourd'hui en 2009, les processeurs sont très bon marché et la mémoire est encore limitée à quelques gigaoctets par l'architecture ou le prix. Si un site a besoin de keep-alive, il y a un vrai problème. Les sites très chargés désactivent souvent la fonction Keep-Alive pour prendre en charge le nombre maximum de clients simultanés. Le vrai inconvénient de ne pas avoir de keep-alive est une latence légèrement accrue pour récupérer des objets. Les navigateurs doublent le nombre de connexions simultanées sur les sites non-keepalive pour compenser cela.

(à partir de http://haproxy.1wt.eu/ )

Est-ce conforme à l'expérience d'autres peuples? c'est-à-dire sans keep-alive - le résultat est-il à peine perceptible maintenant? (il vaut probablement la peine de noter qu'avec les websockets, etc. - une connexion est maintenue "ouverte" quel que soit l'état de maintien en vie de toute façon - pour des applications très réactives). L'effet est-il plus important pour les personnes éloignées du serveur ou s'il y a de nombreux artefacts à charger à partir du même hôte lors du chargement d'une page? (Je pense que des choses comme CSS, images et JS proviennent de plus en plus de CDN compatibles avec le cache).

Pensées?

(Je ne sais pas si c'est une chose serverfault.com, mais je ne vais pas traverser le message jusqu'à ce que quelqu'un me dise de le déplacer là-bas).

Michael Neale
la source
1
Il convient de noter qu'ailleurs dans la documentation de l'haproxy, le keep-alive est mentionné en d'autres termes plus favorables. J'aimerais entendre parler de l'expérience des gens, en particulier pour l'hébergement de masse.
Michael Neale
"Obtenez un serveur Web / d'applications mieux conçu"? :-) Les nouvelles conceptions (comme Jetty) avec la gestion des connexions de continuation (comme) atténuent essentiellement les problèmes de mémoire / thread. Aussi, "quelques Go" sonne comme un terme serveur 2008/2009 ;-)
3
Cela me ressemble à du tapage. Le RTT supplémentaire impliqué dans la mise en place d'une nouvelle prise est une limite physique dure qui est souvent suffisamment longue pour être détectable par un être humain et ne peut pas être réduite dans les lois connues de la physique. À l'inverse, la RAM est bon marché, devient moins chère, et il n'y a aucune raison pour qu'une socket inactive en utilise plus de quelques Ko.
Will Dean
2
mais ce qui est intéressant, c'est que ce n'est pas que de la théorie - c'est l'auteur de l'haproxy. Tout ce que j'entends, ce sont des théories et des hypothèses.
Michael Neale

Réponses:

141

Hé puisque je suis l'auteur de cette citation, je vais répondre :-)

Il y a deux gros problèmes sur les grands sites: les connexions simultanées et la latence. Les connexions simultanées sont causées par des clients lents qui mettent du temps à télécharger le contenu et par des états de connexion inactifs. Ces états de connexion inactifs sont causés par la réutilisation de la connexion pour récupérer plusieurs objets, appelés keep-alive, qui sont encore augmentés par la latence. Lorsque le client est très proche du serveur, il peut faire un usage intensif de la connexion et s'assurer qu'il n'est presque jamais inactif. Cependant, lorsque la séquence se termine, personne ne se soucie de fermer rapidement le canal et la connexion reste ouverte et inutilisée pendant longtemps. C'est la raison pour laquelle de nombreuses personnes suggèrent d'utiliser un délai de maintien en vie très faible. Sur certains serveurs comme Apache, le délai d'expiration le plus bas que vous pouvez définir est d'une seconde, et il est souvent beaucoup trop long pour supporter des charges élevées: si vous avez 20000 clients devant vous et qu'ils récupèrent en moyenne un objet toutes les secondes, vous aurez ces 20000 connexions établies en permanence. 20000 connexions simultanées sur un serveur polyvalent comme Apache sont énormes, nécessiteront entre 32 et 64 Go de RAM selon les modules chargés, et vous ne pouvez probablement pas espérer aller beaucoup plus haut même en ajoutant de la RAM. En pratique, pour 20000 clients, vous pouvez même voir 40000 à 60000 connexions simultanées sur le serveur car les navigateurs essaieront de configurer 2 à 3 connexions s'ils ont de nombreux objets à récupérer. et vous ne pouvez probablement pas espérer aller beaucoup plus haut même en ajoutant de la RAM. En pratique, pour 20000 clients, vous pouvez même voir 40000 à 60000 connexions simultanées sur le serveur car les navigateurs essaieront de configurer 2 à 3 connexions s'ils ont de nombreux objets à récupérer. et vous ne pouvez probablement pas espérer aller beaucoup plus haut même en ajoutant de la RAM. En pratique, pour 20000 clients, vous pouvez même voir 40000 à 60000 connexions simultanées sur le serveur car les navigateurs essaieront de configurer 2 à 3 connexions s'ils ont de nombreux objets à récupérer.

Si vous fermez la connexion après chaque objet, le nombre de connexions simultanées diminuera considérablement. En effet, il baissera d'un facteur correspondant au temps moyen de téléchargement d'un objet par le temps entre les objets. Si vous avez besoin de 50 ms pour télécharger un objet (une photo miniature, un bouton, etc ...), et que vous téléchargez en moyenne 1 objet par seconde comme ci-dessus, alors vous n'aurez que 0,05 connexion par client, soit seulement 1000 connexions simultanées pour 20000 clients.

Maintenant, le temps d'établir de nouvelles connexions va compter. Les clients éloignés connaîtront une latence désagréable. Dans le passé, les navigateurs utilisaient de grandes quantités de connexions simultanées lorsque le maintien en vie était désactivé. Je me souviens des chiffres de 4 sur MSIE et de 8 sur Netscape. Cela aurait vraiment divisé la latence moyenne par objet par autant. Maintenant que le keep-alive est présent partout, nous ne voyons plus ce nombre élevé, car cela augmente encore la charge sur les serveurs distants, et les navigateurs prennent soin de protéger l'infrastructure d'Internet.

Cela signifie qu'avec les navigateurs actuels, il est plus difficile d'obtenir les services non-keep-alive aussi réactifs que les services keep-alive. De plus, certains navigateurs (par exemple: Opera) utilisent des heuristiques pour essayer d'utiliser le pipelinining. Le pipelining est un moyen efficace d'utiliser le keep-alive, car il élimine presque la latence en envoyant plusieurs demandes sans attendre de réponse. Je l'ai essayé sur une page avec 100 petites photos, et le premier accès est environ deux fois plus rapide que sans keep-alive, mais le prochain accès est environ 8 fois plus rapide, car les réponses sont si petites que seule la latence compte (seulement "304" réponses).

Je dirais que, idéalement, nous devrions avoir des paramètres réglables dans les navigateurs pour leur permettre de maintenir les connexions actives entre les objets récupérés et de les supprimer immédiatement lorsque la page est terminée. Mais nous ne voyons pas cela malheureusement.

Pour cette raison, certains sites qui ont besoin d'installer des serveurs à usage général tels qu'Apache sur la face avant et qui doivent prendre en charge de grandes quantités de clients doivent généralement désactiver le keep-alive. Et pour forcer les navigateurs à augmenter le nombre de connexions, ils utilisent plusieurs noms de domaine afin que les téléchargements puissent être parallélisés. C'est particulièrement problématique sur les sites utilisant intensivement SSL car la configuration de la connexion est encore plus élevée car il y a un aller-retour supplémentaire.

Ce qui est plus couramment observé de nos jours, c'est que ces sites préfèrent installer des frontaux légers tels que haproxy ou nginx, qui n'ont aucun problème à gérer des dizaines à des centaines de milliers de connexions simultanées, ils activent le keep-alive côté client et le désactivent sur le Côté Apache. De ce côté, le coût d'établissement d'une connexion est presque nul en termes de CPU, et pas du tout perceptible en termes de temps. De cette façon, cela offre le meilleur des deux mondes: une faible latence due à la persistance avec des délais d'expiration très faibles côté client et un faible nombre de connexions côté serveur. Tout le monde est content :-)

Certains produits commerciaux améliorent encore cela en réutilisant les connexions entre l'équilibreur de charge avant et le serveur et en multiplexant toutes les connexions client sur eux. Lorsque les serveurs sont proches du LB, le gain n'est pas beaucoup plus élevé que la solution précédente, mais il faudra souvent des adaptations sur l'application pour s'assurer qu'il n'y a pas de risque de croisement de session entre utilisateurs en raison du partage inattendu d'une connexion entre plusieurs utilisateurs . En théorie, cela ne devrait jamais arriver. La réalité est bien différente :-)

Willy Tarreau
la source
1
Merci pour la réponse complète et complète! J'ai été légèrement déconcerté par divers commentaires sur la page sur le keep-alive - mais tout cela a du sens.
Michael Neale
Fait intéressant - j'ai observé que Chrome sous Linux réutilise une connexion maintenue en vie pendant quelques secondes - c'est-à-dire le temps qu'il a fallu pour ouvrir un autre onglet - cet autre onglet portait sur un nom d'hôte différent, mais résolu via le caractère générique DNS vers le même serveur (masse hébergement virtuel) - et ainsi réutilisé la même connexion! (cela m'a causé une certaine surprise, pas la bonne sorte - évidemment si garder en vie est uniquement côté client, c'est bien).
Michael Neale
Tout ce que j'ai entendu, c'est "utiliser autre chose qu'apache et ce n'est pas grave". Ce que j'ai extrapolé était "désactiver mod_php et passager et même apache pourrait avoir une chance de se battre".
coolaj86
@ CoolAJ86: il ne s'agit absolument pas de dénigrer Apache, et je l'utilise personnellement. Le fait est que plus le serveur est générique, moins vous avez d'options à mettre à l'échelle. Certains modules nécessitent le modèle pré-fourche, vous ne pouvez donc pas évoluer vers un grand nombre de connexions. Mais comme expliqué, ce n'est pas un gros problème car vous pouvez le combiner avec un autre composant gratuit comme haproxy. Pourquoi quelqu'un remplacerait-il tout dans ce cas? Mieux vaut installer haproxy que de devoir réimplémenter votre application à l'aide d'un autre serveur!
Willy Tarreau
22

Depuis que cela a été écrit (et publié ici sur stackoverflow), nous avons maintenant des serveurs tels que nginx qui gagnent en popularité.

nginx, par exemple, peut contenir 10 000 connexions persistantes ouvertes en un seul processus avec seulement 2,5 Mo (mégaoctets) de RAM. En fait, il est facile de maintenir ouvertes plusieurs milliers de connexions avec très peu de RAM, et les seules limites que vous atteindrez seront d'autres limites telles que le nombre de descripteurs de fichiers ouverts ou de connexions TCP.

Keep-alive était un problème non pas à cause d'un problème avec la spécification keep-alive elle-même, mais à cause du modèle de mise à l'échelle basé sur les processus d'Apache et des keep-alives piratés dans un serveur dont l'architecture n'a pas été conçue pour l'accueillir.

Apache Prefork + mod_php + keep-alives est particulièrement problématique. Il s'agit d'un modèle dans lequel chaque connexion continuera à occuper toute la RAM occupée par un processus PHP, même si elle est complètement inactive et ne reste ouverte qu'en tant que keep-alive. Ce n'est pas évolutif. Mais les serveurs n'ont pas besoin d'être conçus de cette façon - il n'y a aucune raison particulière pour qu'un serveur ait besoin de garder chaque connexion persistante dans un processus séparé (surtout pas lorsque chaque processus de ce type a un interpréteur PHP complet). PHP-FPM et un modèle de traitement de serveur basé sur les événements comme celui de nginx résolvent le problème avec élégance.

Mise à jour 2015:

SPDY et HTTP / 2 remplacent la fonctionnalité de maintien en vie de HTTP par quelque chose d'encore mieux: la possibilité non seulement de maintenir une connexion active et de faire plusieurs requêtes et réponses, mais aussi de les multiplexer, afin que les réponses puissent être envoyées dans n'importe quel ordre , et en parallèle, plutôt que seulement dans l'ordre où ils ont été demandés. Cela empêche les réponses lentes de bloquer les plus rapides et supprime la tentation pour les navigateurs de maintenir ouvertes plusieurs connexions parallèles vers un seul serveur. Ces technologies mettent en évidence les insuffisances de l'approche mod_php et les avantages de quelque chose comme un serveur Web basé sur les événements (ou à tout le moins, multi-thread) couplé séparément à quelque chose comme PHP-FPM.

thomasrutter
la source
2

ma compréhension était que cela n'avait pas grand-chose à voir avec le processeur, mais la latence dans l'ouverture de sockets répétées à l'autre bout du monde. même si vous avez une bande passante infinie, la latence de connexion ralentira l'ensemble du processus. amplifié si votre page contient des dizaines d'objets. même une connexion persistante a une latence requête / réponse, mais elle est réduite lorsque vous avez 2 sockets car en moyenne, l'une devrait diffuser des données tandis que l'autre pourrait bloquer. De plus, un routeur ne supposera jamais qu'une socket se connecte avant de vous laisser écrire dessus. Il a besoin de la poignée de main aller-retour complète. encore une fois, je ne prétends pas être un expert, mais c'est ainsi que je l'ai toujours vu. ce qui serait vraiment cool, c'est un protocole entièrement ASYNC (non, pas un protocole complètement malade).

catchpolenet
la source
ouais - ce serait mon hypothèse. c'est peut-être un compromis - il y a un point où la latence (due à la distance) signifie que c'est un vrai problème
Michael Neale
ok, la typographie moderne vous obligerait à vous connecter à un proxy proche (peut-être). mais alors étendez-vous la question à savoir si les mandataires doivent utiliser des connexions persistantes?
catchpolenet
@Michael Neale également, à cause de choses comme le démarrage lent de TCP, la pénalité de latence réelle est bien pire que ce à quoi vous vous attendez.
MartinodF
peut-être que le compromis est un délai d'expiration beaucoup plus court. si vous avez des demandes sauvegardées, pourquoi arrêter le socket et recommencer? même 1 seconde permettrait à une page de se charger avec une persistance totale, puis d'éteindre les sockets immédiatement après.
catchpolenet
2

Des keep-alives très longs peuvent être utiles si vous utilisez un CDN "origin pull" tel que CloudFront ou CloudFlare. En fait, cela peut s'avérer plus rapide que pas de CDN, même si vous diffusez un contenu complètement dynamique.

Si vous avez une longue durée de vie telle que chaque PoP a fondamentalement une connexion permanente à votre serveur, la première fois que les utilisateurs visitent votre site, ils peuvent faire une poignée de main TCP rapide avec leur PoP local au lieu d'une lente prise de contact avec vous. (La lumière elle-même prend environ 100 ms pour parcourir la moitié du monde via la fibre, et l'établissement d'une connexion TCP nécessite la transmission de trois paquets dans les deux sens. SSL nécessite trois allers-retours .)

mjs
la source
1
J'ai été tenté de +1, mais votre deuxième paragraphe contient cette remarque incorrecte de lumière qui ne prend que 10 ms pour parcourir la moitié du monde. 10 ms de vitesse de la lumière dans le vide font 3000 km et 10 ms de la vitesse de la lumière dans une fibre ne sont pas beaucoup plus de 2000 km; à l'autre bout du monde (le long de la surface) est de 20 000 km. Ce serait donc 100 ms - si seulement votre fibre allait directement de Londres à Sydney plutôt que de faire le tour de l'Afrique par voie maritime ou de prendre la longue route par Hawaï ...
pyramides
@pyramids Vous avez raison, soit j'ai tapé là-dessus, soit j'ai juste fait une gaffe. Mettra à jour.
mjs le