Programmation performante des serveurs web

La performance des serveurs web est un vrai sujet : on a besoin de sites qui répondent vite, pour vendre plus 😉

Et un serveur web peut être amené à répondre à « pleins » de requêtes simultannées.

On peut palier de manière superficielle à ces problèmes avec du cache. Bonne idée… mais les sites doivent de plus en plus être personnalisés, dynamiques. On peut donc « cacher » certaines ressources, mais pour les parties dynamiques, il faut faire autrement.

Au coeur des serveurs web se pose donc la question de la programmation parallèle : faire tourner plusieurs programmes en parallèles pour répondre en même temps à plusieurs utilisateurs.

Quand on programme avec les technologies « normales », on ne se rend pas compte de tout ça : c’est le serveur web qui gère, de manière transparente pour le programmeur. en fait, à chaque appel au serveur web, un fork est créé. Un fork, c’est une unité d’exécution indépendante. Chaque programme, pour chaque appel, est donc « dans son coin ».

C’est une bonne idée, parce que on masque ce problème technique… Sauf que la gestion des forks n’est pas très optimisée, et qu’à créer trop de forks, on fait rapidement tomber les serveurs.

D’ou l’idée de faire autrement. C’est en particulier ce que propose certains environnements, tel node.js, basés sur des serveurs webs spécifiques (autre que apache donc).

Dans ces environnements alternatifs, les différents programmes ne sont pas exécutés dans des forks indépendants.

Pour gérer la continuité entre les différentes fonction, le programmeur doit mettre en place des procédures de call back, …

Bref, je ne veux pas trop rentré dans les détails (d’ailleurs, je ne suis plus programmeur, j’atteints vite mes limites 😉 ).

Mon sentiment sur cette histoire :

Je comprends le chemin emprunté par ces solutions, c’est pragmatique : puisque les serveurs web rament avec les technos de forks, on descend d’un niveau, et on gère la programmation parallèle directement.

Mais sur le fond, je suis convaincu que la solution n’est pas là. Cela me semble complètement indispensable de masquer ces problématiques au programmeur, qui a déjà bien de quoi s’occuper. Et si la programmation parallèle n’est pas efficace, et bien il faut revoir l’architecture du système !

12 commentaires

  1. Les serveurs web, n’ont aucun soucis avec les threads, qui d’ailleurs sont gérés par l’OS au niveau du scheduling et non par la partie applicative.

    Puisque tu mentionnes Apache, je pense que tu confonds thread et fork d’ailleurs. Apache couplé avec PHP fonctionnant avec le module prefork en très grande majorité, soit des forks.

    Il faut savoir qu’un fork n’est pas un thread. En « forkant » le programme se clone intégralement en mémoire pour prendre un chemin d’execution différent. C’est lent à la création, ça consomme de la mémoire, mais c’est très simple de fonctionnement. Chaque fork est totalement indépendant des autres. C’est ce que tu sembles vouloir décrire dans ton post.
    Dans les threads une connexion et une mémoire partagée est gardée avec son parent. Ce qui en simplifie la création et ouvre la porte à une communication inter-processus. Ce qui peut s’avérer pratique mais cela ouvre aussi la porte à la gestion des lock, mutex, zone de synchronisation de la mémoire partagée… qui entraine bien souvent blocage, goulot d’étranglement etc… il n’y a qu’à regarder le Java et ces nombreux soucis à ce niveau. C’est un sujet difficile à maitriser et assez ouvert aux erreurs.

    Il existe d’autre mode de fonctionnement permettant de gérer ou simuler du multi processus comme les green-threads, co-routines, etc…, avec d’autres avantages mais aussi d’autres inconvénients. La plupart offre une gestion plus rapprochée du langage de programmation et indépendante de l’OS, généralement plus efficace et plus facile à programmer mais au prix d’un fonctionnement bien moins souple.

    Il faut comprendre la nature même du web qui depuis toujours pour le HTTP est un protocole déconnecté et sans état. Chaque requête est indépendante de ses consoeurs et le fork se prette magnifiquement à ce type de fonctionnement contrairement aux autres opportunités. De plus, les sessions et cookies apportent le minimum pour palier au besoin rudimentaire de garder un état entre deux requêtes.

    Seulement aujourd’hui le volume de requête, les processeurs multi-coeur, l’arrivée du web dit connecté, entre autre mais pas seulement, ajoutent de nouvelles inconnues à l’équation.
    Aujourd’hui les technologies s’adaptent et proposent de nouvelle solution. C’est pourquoi dans Php d’autres solutions se sont présentées comme FPM, German, utilisation de ZeroMQ pour ne citer que cela…

    « Masquer le problème » est un terme incorrect. Je dirai qu’à leur actuelle la gestion des processus est reportée au serveur web par exemple.
    Pour un programmeur il vaut mieux être au fait de ces éléments même si il ne les emplois pas directement dans le développement web. Chose qui devrait changer car de plus en plus on a le besoin justement d’apporter une communication interprocess ; peu importe comment ce fait celle-ci.

    Bref la « scalabilité » d’un système, prend en compte un nombre très large d’éléments à commencer par les besoins du dit système et ne se limite pas à la question du thread ou pas thread. C’est une problématique à abordée dans son ensemble et pour ma part, elle ne se réfléchie pas comme tu viens de le faire.

  2. Merci Alban,

    Effectivement, tu as raison, j’ai confondu Thread et Fork, j’ai corrigé l’article suite à ton commentaire

    Merci encore pour ces infos, c’est le côté génial du blog, que d’avoir de tels commentaires 🙂

  3. Je ne suis pas d’accord sur ton sentiment:

    node.js et cie n’est pas juste un nouveaux serveur web un peu plus performant que les autres. Il s’agit d’un changement de paradigme : la programmation asynchrone. Cela permet de découpler l’écriture du programme de la gestion du parallélisme. Cela permet de distribuer plus facilement les traitements sur les processeurs et serveurs disponible.

    C’est pour moi un changement d’architecture qui permet justement de se détacher des problèmes liés à l’exécution concurrente, au prix d’une nouvelle façon de penser et d’écrire les programmes.

  4. @Nicolas> Oui, j’avais bien compris ce changement de paradigme. Mais ça me donne l’impression que ce changement rajoute plutôt de la complexité côté programmation, avec comme raison la performance du serveur.

  5. Je t’en prie 🙂

    N’oublie pas que Apache ne se restreint pas à son module prefork (gestion par fork) mais il à aussi une gestion par thread (module worker), on peut aussi lui indiquer une gestion externe des process (exemple avec FPM).
    On restreint beaucoup trop souvent Apache à son utilisation la plus courante malheureusement.

    De même certains langage dans la conception de leur interpréteur sont plus apte que d’autre à la gestion multi process. Le GIL (Global Interpreter Lock) est généralement un point noir en la matière. Le moteur V8 de Google sur lequel se base sur Node.js est conçu pour être performant à ce niveau.

    http://fr.wikipedia.org/wiki/C10k_problem est un bon point de départ sur le sujet des perf des serveurs web.

    Aujourd’hui je dirai que la techno en terme de serveur web importe moins qu’avant. Même si elle reste très importante, elle n’est plus cruciale, due au progrès apportés en matière de « cloud ». Aujourd’hui on multiplie les instances de serveurs en un claquement de doigt, tout en gardant les développement simple et avec une gestion fine des coûts…

  6. @Alban> Merci pour le lien, je ne connaissais pas et c’est effectivement super intéressant.

    A propos du cloud : a court terme, tu as raison, mais je pense que la « pensée écolo », la limite des ressources naturelles va changer ça dans les années à venir.

  7. Merci, il se pourrait que le cloud soit justement une des réponses à la « montée écolo » qui rappelle le besoin accrue de limiter les consommations d’énergie.

    Je m’explique sur ce point de vue :

    Avoir 1000 instances d’un serveur web aujourd’hui ne signifie pas qu’il y a 1000 servers physique derrière (de l’ordre d’un 2U mono Xeon par exemple) comme dans le passé.

    Aujourd’hui des centaines d’instances peuvent être gérées par une seule machine physique (de l’ordre par exemple d’un 64 coeur avec 256 Go de ram couplé à une bais de disque).

    Ce genre de catégorie de machine très puissantes et très onéreuse, hier réservées à de grands compte, sont aujourd’hui massivement employée pour établir des clouds. À terme on peut estimer que dans les datacenters des milliers de petits serveurs vont être remplacer par des « super-calculateurs » offrant à surface identique, énergie identique, une capacité de service beaucoup plus importante.

    Plus le cloud progresse dans ses technologies, plus les ressources physiques sont idéalement réparties et utilisées, moins ont a besoin d’en produire et de les alimentées (à besoin égaux mais ceux-ci sont loins de ce stabiliser n’est-ce pas…). De plus en plus on loue du calcul, de la mémoire, de l’espace disque, on ne loue plus un local en datacentre, une machine physique.

    De même les datacenters font preuvent d’innovation en matière de refroidissement, 50 % de l’énergie consommée rappelons le, OVH en france avec sont dernier datacenter est bien placé sur ce créneau, et c’est une bonne chose d’une manière générale.

    Ceci dit, je ne sais pas de quoi l’avenir sera fait, tant qu’on consomme moins ! Et surtout qu’on le fasse à moindre coût pour la planète.

  8. le serveur NGinx (dont tu avais parlé il me semble il y a quelques mois) permet d’éviter la multiplication des process (ou fork) en gérant tous les traitements en un minimum de threads / processus (environ 4 ou 5 pour un serveur php classique).

  9. @Olivier> Hello Mr Olivier 🙂

    Oui, on a donc plusieurs paramètres pour aider à faire des serveurs performants (serveur HTTP, Langage de programmation, …). J’ai peut être tord, mais je trouve que tout ça est encore d’assez bas niveau : ça marche mais faut fouiller et retrousser ses manches. Je pense qu’à l’avenir, pas forcément proche, on aura des couches d’abstractions plus évoluées.

  10. @François

    Je doute pour ma part que l’abstraction soit une solution. l’abstraction induit une certaine paresse de la part des développeurs. On le voit en Java ou l’abstraction de la gestion mémoire produit des conso mémoire délirantes : on voit réapparaitre des OoM (out of memor) comme aux temps glorieux des débuts de J2EE (souviens toi Wokup 🙂 )
    IL ya surement moyen de concevoir un langage / couche d’abstraction qui faciliterait l’execution asynchrone plutot que synchrone mais sans réelle compréhension de ces modes d’exécution et de leurs implications au niveau système on tombera rapidement dans des travers.

    Ceci si tu as une idée même vague de ce que tu aimerais voir émerger, je veux bien apporter mes connaissances techniques 🙂

  11. Tu as peut être raison, on verra.

    Pour ma part, je suis sur un nouveau projet de startup, toujours du logiciel, mais fini les projets « universels » : il s’agit du développement d’une brique tout ce qu’il y a de plus métier 😉 A suivre, sur ce blog ou ailleurs 🙂

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *