7 points par GN⁺ 2025-07-07 | 4 commentaires | Partager sur WhatsApp
  • Il est confirmé par expérimentation que les programmes CGI, largement utilisés aux débuts du Web, peuvent encore offrir de hautes performances sur du matériel moderne
  • Le CGI traite les requêtes par processus, ce qui permet une gestion automatique de la mémoire et offre l’avantage d’un déploiement simple
  • Les résultats des benchmarks démontrent qu’un serveur ordinaire avec un CPU à 16 threads peut traiter plus de 2 400 requêtes par seconde, soit plus de 200 millions par jour
  • Le code d’exemple guestbook.cgi, écrit en Go et SQLite, ainsi que le Dockerfile, sont publiés en open source
  • Bien que le CGI ne soit plus couramment utilisé aujourd’hui, l’article montre qu’il peut rester une alternative pratique et moderne

Programmes CGI et principe de fonctionnement

  • Au début des années 2000, les programmes CGI (Common Gateway Interface) constituaient la principale méthode pour créer des sites web dynamiques
  • Ils étaient le plus souvent écrits en Perl ou en C, le C étant parfois choisi pour améliorer les performances
  • Le concept du CGI est simple mais puissant
    • Le serveur web place dans des variables d’environnement les métadonnées de la requête (en-têtes HTTP, requête, etc.)
    • Il crée un processus distinct pour exécuter le programme CGI
    • Il transmet le corps de la requête via stdin
    • Il capture le stdout du programme comme réponse HTTP
    • Il envoie la sortie stderr dans les logs d’erreur du serveur
    • Une fois la requête terminée, le processus s’arrête et les descripteurs de fichiers ainsi que la mémoire sont libérés automatiquement
  • Du point de vue des développeurs, déployer une nouvelle version était aussi extrêmement simple : il suffisait de copier le fichier dans le répertoire cgi-bin/

Hug of death (explosion du trafic)

  • Au début des années 2000, la plupart des serveurs web fonctionnaient couramment avec 1 à 2 CPU et 1 à 4 Go de mémoire
  • En raison de l’architecture du serveur web Apache, qui crée un processus httpd par connexion via fork, les besoins en mémoire augmentaient avec le nombre de connexions
  • Il était difficile de dépasser 100 connexions simultanées, et un simple lien depuis un site populaire suffisait souvent à surcharger le serveur
    • ( Slashdot Effect : lorsqu’un lien apparaissait sur le très populaire Slashdot à l’époque, le trafic affluait d’un coup. C’est comparable aujourd’hui à une mise en avant en tête de Hacker News)

Le CGI dans un environnement serveur moderne

  • Il existe désormais des serveurs dotés de 384 threads CPU, et même de petites VM peuvent proposer 16 CPU
  • Les performances CPU et mémoire ont fortement progressé
  • Comme les programmes CGI reposent sur des processus distincts, ils peuvent exploiter naturellement le multicœur
  • C’est pour cette raison qu’un benchmark a été mené afin de mesurer directement la rapidité des programmes CGI sur du matériel moderne
  • L’expérience a été réalisée sur un serveur AMD 3700X (16 threads)

Principaux résultats des benchmarks

  • Un programme CGI simple a été testé à la fois avec Apache et avec un serveur Go net/http
  • Description du programme guestbook.cgi

  • L’outil de génération de charge HTTP plow a été utilisé pour envoyer 100 000 requêtes avec 16 connexions
  • Même sur du matériel ordinaire, il est possible de traiter plus de 2 400 requêtes par seconde, soit plus de 200 millions de requêtes par jour
  • Le CGI n’est plus dominant aujourd’hui, mais il reste utilisable en production réelle
  • Benchmark d’écriture dans l’environnement Apache

    • Traitement d’environ 2 468 requêtes par seconde, avec une latence moyenne de 6,47 ms
    • 100 000 requêtes POST ont été traitées en seulement 40,5 secondes
    • La plupart des requêtes ont reçu une réponse en 7 ms, et une infime minorité seulement a dépassé 100 ms
    • Cela démontre en pratique d’excellentes performances en écriture
  • Benchmark de lecture dans l’environnement Apache

    • Traitement d’environ 1 959 requêtes par seconde, avec une latence moyenne de 8,16 ms
    • 100 000 requêtes GET ont été traitées en 51 secondes
    • Plus de la moitié des requêtes ont été servies en moins de 8 ms, et la latence maximale n’a atteint que 31 ms
    • Les performances en lecture sont elles aussi largement satisfaisantes
  • Benchmark d’écriture dans l’environnement Go net/http

    • Traitement d’environ 2 742 requêtes par seconde, avec une latence moyenne de 5,83 ms
    • 100 000 requêtes POST ont été traitées en 36,4 secondes
    • Le débit atteint en moyenne 2 742 RPS, avec une latence moyenne de 5,8 ms, soit de meilleurs chiffres qu’Apache
    • Plus de 95 % des requêtes ont été traitées en moins de 6 ms
    • Le CGI dans l’environnement Go offre lui aussi des performances suffisantes pour un usage réel
  • Benchmark de lecture dans l’environnement Go net/http

    • Traitement d’environ 2 469 requêtes par seconde, avec une latence moyenne de 6,47 ms
    • 100 000 requêtes GET ont été traitées en 40,4 secondes
    • La plupart des requêtes peuvent être servies en moins de 7 ms
    • Le débit en lecture et le temps de réponse sont comparables ou supérieurs à ceux d’Apache

Conclusion et liens

  • Les programmes CGI conservent des atouts sur du matériel récent : concurrence très élevée, déploiement simple et libération automatique des ressources par le système d’exploitation
  • Comparés aux frameworks modernes, ils restent extrêmement simples, mais peuvent encore être utilisés en production pour des services d’une certaine taille
  • L’exemple de livre d’or et les données de benchmark sont publiés sur le GitHub ci-dessous
    https://github.com/Jacob2161/cgi-bin

4 commentaires

 
kansm 2025-07-09

Oh là là… On va se remettre à utiliser du CGI ?? haha
Waouh… ça date de quand, le CGI…

 
tujuc 2025-07-08

Il y a du nouveau mis à jour à la date du 7/7.

Serving a half billion requests per day with Rust + CGI

500 millions de requêtes, quand même...

 
GN⁺ 2025-07-07
Avis Hacker News
  • Souvenir d’environnements des années 1990 où des programmes CGI écrits en C étaient vraiment très rapides, tout en reconnaissant que leur défaut était de générer beaucoup d’erreurs ; les programmes modernes en Go ou dans des langages comme Nim mentionnés dans l’article donnent aussi une impression de grande vitesse et de faible latence en localhost tant qu’ils ne se connectent pas à une base de données, un peu comme utiliser fork & exec dans un utilitaire CLI ; comparé à la latence réseau, le coût était presque négligeable

    • Mention aussi d’une culture qui pousse facilement à devenir dépendant d’une technologie précise ; par exemple, une fois habitué à des langages au coût de démarrage élevé comme l’interpréteur Python, on finit par considérer qu’un modèle multishot ou persistant est nécessaire

    • Le modèle one-shot des débuts de HTTP venait à l’origine du manque de mémoire des serveurs FTP, qui ne pouvaient pas conserver longtemps des centaines de sessions de connexion inactives

    • Mention de la possibilité d’une excellente conception système en combinant le pre-forking en CGI (qui peut masquer la latence) et un langage sûr comme Rust ; la terminaison TLS peut être gérée dans un serveur web multithreadé, ou dans une couche comme CloudFront, ce qui est présenté comme très pratique

      • Environnement sans état résiduel, où les core dumps et le débogage sont très faciles, et où l’on peut surtout faire évoluer le système simplement avec un modèle de requête linéaire
      • Éloge de la simplicité consistant à lire sur stdin et écrire sur stdout ; les WebSockets ajoutent un peu de complexité, mais rien d’inquiétant
      • Rappel du basculement rapide vers les serveurs d’applications, motivé par le coût de fork() et par la volonté d’éviter les dangers du C lors de l’essor de Java ; on peut maintenant revenir à la simplicité
      • Même sans aimer Rust, si une époque arrive où l’on peut écrire facilement du code de backend web de cette manière, cela pourrait aussi devenir attractif pour les développeurs node/js, php et python
  • Témoignage d’un développeur ayant commencé à l’époque du CGI et ayant gardé une forte aversion pour l’idée de lancer des sous-processus de courte durée

    • Explication du fait que PHP et FastCGI ont été créés pour échapper au problème de performance consistant à lancer un nouveau processus à chaque requête web

    • Avec les progrès récents du matériel, il s’est rendu compte que le coût de démarrage d’un processus n’est en réalité pas un si gros problème

    • Ce benchmark peut traiter 2 000 requêtes par seconde, et même quelques centaines suffisent dans un environnement moderne où il est facile de scaler sur plusieurs instances

    • Accord avec l’idée décrivant AWS Lambda comme une renaissance du modèle CGI, comparaison jugée assez juste

    • Si les scripts CGI avaient été déployés sous forme de binaires C à liaison statique, en faisant même attention à leur taille, la déception aurait sans doute été moindre

      • Le coût de démarrage d’un processus avec liaison dynamique, chargement de l’interpréteur PHP, de diverses bibliothèques et parsing de fichiers est bien plus élevé
      • Conviction que l’approche consistant à utiliser Go aurait déjà été très compétitive il y a 25 ans
      • L’ouverture d’une base SQLite a des performances presque comparables au passage d’un socket via un changement de contexte, et reste bien plus rapide qu’une connexion mysql distante
      • FastCGI reste selon lui un excellent choix, même pour de nouvelles applications
    • Le CGI n’était ni financièrement ni techniquement très coûteux dans des environnements à faible charge

      • En situation de forte charge, des processus persistants comme FastCGI sont plus avantageux
      • Le CGI peut aussi monter jusqu’à 2 000 rps, mais FastCGI peut atteindre des performances bien supérieures
      • Il suffit d’ajouter un processus serveur dédié et de ne redémarrer qu’au moment des mises à jour, ce qui vaut le coup quand la performance compte
    • Avant l’arrivée de Go, dans les années 2000, écrire des programmes CGI en C/C++ était difficile à la fois en matière de sûreté et de développement

      • Perl et Python avaient un coût important de démarrage de l’interpréteur et de compilation, et Java était en pratique encore plus lent
      • Accord sur le fait que AWS Lambda ressemble à une réincarnation du modèle CGI
      • Impression qu’on est revenu aujourd’hui à un modèle presque identique à un FastCGI managé
      • Regret face au déluge de technologies qui ajoutent énormément de complexité alors qu’il suffirait d’uploader un exécutable et de le lancer
  • Aujourd’hui, on vit à une époque où des serveurs disposent de 384 threads CPU, et même de petites VM peuvent avoir 16 CPU

    • Sur ce type de matériel, développer avec Kestrel permettrait sans difficulté de traiter des milliers de milliards de requêtes par jour

    • Il serait possible d’offrir une expérience de développement proche de PHP grâce à l’opérateur d’interpolation de chaînes

    • Avec LINQ et String.Join(), on peut facilement templatiser des tableaux HTML et des éléments imbriqués

    • La vraie difficulté est surtout de savoir éviter les pièges de l’écosystème comme MVC/Blazor/EF

    • Il est même possible d’exécuter tout le programme comme un seul fichier de niveau supérieur depuis le CLI, mais sans connaître le mot-clé « Minimal APIs », on risque facilement de se perdre dans un labyrinthe de mauvaise documentation

      • Étonnement devant le grand nombre de cas où des couches d’abstraction sont ajoutées au-dessus des technologies cœur pour obtenir des promotions vers des postes de Director ou VP
  • L’avantage du CGI est qu’en environnement multitenant, il n’est pas nécessaire de reconstruire des primitives d’isolation

    • Même si une requête contient un bug, l’isolation par processus empêche qu’elle affecte les autres requêtes
    • Même une boucle infinie ne mène pas à un déni de service (DoS) grâce à l’ordonnancement préemptif
    • On peut forcer l’arrêt des requêtes trop longues avec rlimit
    • Avec cgroup, il est possible d’allouer équitablement par tenant l’usage mémoire, CPU, IO disque/réseau
    • Les namespaces/jails et la séparation des privilèges permettent de restreindre les droits d’accès à chaque requête
  • Grâce aux scripts CGI, perl a été optimisé pour un démarrage rapide

    • En exécutant la commande time perl -e '', on constate que perl démarre en 5 ms, contre 33 ms pour python3 et 77 ms pour ruby

      • Mention du fait que des scripts du branch tcc mob utilisant #!/bin/tcc -run sont 1,3 fois plus rapides que perl
      • Cas de Julia, de la VM Java ou de PHP en mode thread, où les temps de démarrage deviennent eux aussi très longs
      • Observation que les gens finissent par dépendre machinalement de « gros environnements »
      • Dans la communauté Lisp aussi, cela se répète avec l’usage d’images, et c’est de là qu’est né le mème « emacs is bloated »
      • L’âge d’or de Perl à la fin des années 1990 a vraiment été rendu possible par le CGI
      • Souvenir d’une époque où même getline n’était pas standard, et où l’on écrivait des bibliothèques C tierces de plusieurs centaines à plusieurs milliers de lignes
      • Au final, les technologies sont choisies selon leur « réputation », et l’apprentissage se fait le plus souvent par recommandation d’amis
  • En utilisant apache tomcat 11, il suffit d’uploader via ssh un fichier .jsp ou une application java servlet complète (.war) pour que cela fonctionne directement

    • Les performances maximales sont obtenues grâce à une JVM partagée unique

    • Les pools de connexions DB, caches, etc. peuvent aussi être partagés entre applications

    • Expérience jugée vraiment impressionnante

      • Tout dépend toutefois des usages réels

      • Excellent pour les services à grande échelle, mais si 50 petites applications ne traitent chacune que quelques centaines de requêtes par jour, le surcoût mémoire de Tomcat est bien trop élevé par rapport à Apache/Nginx avec scripts CGI

      • Nostalgie de l’époque où déployer consistait simplement à copier un fichier

      • Regret de voir le processus de déploiement devenir si complexe

      • Partage d’expérience d’une utilisation toujours très agréable de Jetty pour des webapps backend

      • Impression que la stack Tomcat/Jakarta EE/JSP est étonnamment solide

      • On peut mélanger HTML et code comme en PHP, ou bien utiliser des routes Java pures

      • Support des WebSockets et modèle single-process multithread, avec des atouts pour la communication temps réel

      • Si nécessaire, on peut partager des données par requête, tandis que le code JSP reste limité par défaut au scope de la requête

      • Le déploiement est vraiment simple : il suffit d’envoyer un nouveau fichier dans le répertoire webapps, et Tomcat charge automatiquement la nouvelle application tout en déchargeant l’ancienne

      • Inconvénient : les fuites de classloader peuvent empêcher le garbage collection, destin classique du modèle single-process

  • Création d’un outil de visualisation des requêtes apache : ibrahimdiallo.com/reqvis

    • Meilleure expérience sur navigateur desktop
    • Permet de voir sur le web le fonctionnement réel à partir de données de trafic HN
  • Il y avait des doutes sur la tendance actuelle vers des architectures complexes ; avec un bon matériel, il est peut-être en réalité possible de continuer à utiliser des technologies plus anciennes

    • À la question de concevoir un système diffusant des informations boursières en temps réel à des millions de personnes, la première idée aurait été une structure de streaming complexe avec Kafka, pubsub, etc., mais la possibilité d’une approche simple basée sur des fichiers statiques sur le serveur a fini par être envisagée

    • Curiosité sur le coût réel d’exploitation d’une telle approche

      • En pratique, la latence de presque toutes les API web est déterminée par les requêtes DB ou les requêtes vers des modèles ML
      • Le reste du processus est négligeable, même en utilisant un langage lent comme Python
      • Si l’on ne renvoie que des données qui changent peu, on peut facilement atteindre les limites du NIC
  • Mise en avant de la ressemblance avec une architecture serverless, mais en beaucoup plus simple et moins cher

    • Question sur l’existence de cas d’usage réels en entreprise
  • Regret qu’au lieu de reconsidérer ces structures traditionnelles, on ait simplement inventé un nouveau paradigme appelé « serverless functions »

    • Même si des fonctions serverless comme Lambda disposent de mécanismes de protection supplémentaires comme des micro-VM, l’idée est qu’en pratique, avec CGI et un simple ajustement des privilèges, on aurait pu aller très loin avec bien moins de complexité
 
regentag 2025-07-07

Bon, pour CGI passe encore, mais la réaction à JSP est surprenante lol
Est-ce que JSP est déjà devenu à ce point une relique de l’Antiquité ?