7 Punkte von GN⁺ 2025-07-07 | 4 Kommentare | Auf WhatsApp teilen
  • Durch ein Experiment wurde bestätigt, dass CGI-Programme, die in der frühen Web-Ära weit verbreitet waren, auf moderner Hardware noch immer hohe Leistung erzielen können
  • CGI verarbeitet Requests pro Prozess, wodurch die Speicherverwaltung automatisch erfolgt und Deployments einfach bleiben
  • Benchmark-Ergebnisse zeigen, dass selbst ein gewöhnlicher Server mit 16 CPU-Threads mehr als 2.400 Requests pro Sekunde und über 200 Millionen Requests pro Tag verarbeiten kann
  • Beispielcode für guestbook.cgi, geschrieben in Go und SQLite, sowie ein Dockerfile wurden als Open Source veröffentlicht
  • CGI wird heute nicht mehr häufig verwendet, zeigt hier aber, dass es nach wie vor eine praktische und moderne Alternative sein kann

CGI-Programme und ihre Funktionsweise

  • Anfang der 2000er waren CGI(Common Gateway Interface)-Programme der wichtigste Weg, dynamische Websites zu bauen
  • Die meisten wurden in Perl oder C geschrieben; zur Leistungssteigerung fiel die Wahl teils auf C
  • Das CGI-Konzept ist einfach, aber wirkungsvoll
    • Der Webserver setzt Request-Metadaten (HTTP-Header, Query usw.) als Umgebungsvariablen
    • Er startet einen separaten Prozess und führt das CGI-Programm aus
    • Der Request-Body wird über stdin übergeben
    • stdout des Programms wird als HTTP-Response erfasst
    • stderr-Ausgaben werden an das Server-Error-Log weitergeleitet
    • Wenn das Programm den Request beendet, endet auch der Prozess, sodass File-Deskriptoren und Speicher automatisch freigegeben werden
  • Aus Sicht von Entwicklern war auch das Deployment neuer Versionen extrem einfach: Es reichte, die Datei in das Verzeichnis cgi-bin/ zu kopieren

Hug of death (Traffic-Ansturm)

  • Anfang der 2000er waren bei Webservern meist Umgebungen mit 1–2 CPUs und 1–4 GB RAM üblich
  • Da der Apache-Webserver strukturell für jede Verbindung einen httpd-Prozess forkte, stieg der Speicherbedarf bei vielen Verbindungen an
  • Mehr als 100 gleichzeitige Verbindungen waren oft schwer zu bewältigen, und schon ein Link von einer bekannten Website konnte einen Server leicht überlasten
    • ( Slashdot Effect : Wenn damals ein Link auf dem populären Slashdot erschien, strömte der Traffic herein. Vergleichbar damit, heute auf der Startseite von Hacker News zu landen)

CGI in modernen Serverumgebungen

  • Heute gibt es bereits Server mit 384 CPU-Threads, und selbst relativ kleine VMs können 16 CPUs bereitstellen
  • CPU- und Speicherleistung haben sich massiv verbessert
  • Da CGI-Programme auf separaten Prozessen basieren, können sie Multicore-Systeme ganz natürlich nutzen
  • Deshalb wurde per Benchmark direkt getestet, wie schnell CGI-Programme auf moderner Hardware tatsächlich sind
  • Das Experiment wurde auf einem Server mit AMD 3700X (16 Threads) durchgeführt

Zentrale Benchmark-Ergebnisse

  • Ein einfaches CGI-Programm wurde sowohl unter Apache als auch mit einem Go-net/http-Server getestet
  • Beschreibung des Programms guestbook.cgi

  • Mit dem HTTP-Load-Generator plow wurden 100.000 Requests mit 16 Verbindungen ausgeführt
  • Selbst auf gewöhnlicher Hardware sind mehr als 2.400 Requests pro Sekunde möglich, also über 200 Millionen Requests pro Tag
  • CGI ist heute zwar nicht Mainstream, kann aber weiterhin auch im realen Betrieb eingesetzt werden
  • Write-Benchmark unter Apache

    • Verarbeitung von rund 2468 Requests pro Sekunde bei einer durchschnittlichen Response-Latenz von 6,47 ms
    • 100.000 POST-Requests wurden in nur 40,5 Sekunden verarbeitet
    • Die meisten Requests wurden innerhalb von 7 ms beantwortet, nur sehr wenige überschritten 100 ms
    • Damit wurde in der Praxis eine hohe Write-Performance nachgewiesen
  • Read-Benchmark unter Apache

    • Verarbeitung von rund 1959 Requests pro Sekunde bei einer durchschnittlichen Response-Latenz von 8,16 ms
    • 100.000 GET-Requests wurden in 51 Sekunden verarbeitet
    • Mehr als die Hälfte der Requests blieb unter 8 ms, die maximale Latenz lag nur bei 31 ms
    • Auch die Read-Performance ist klar ausreichend
  • Write-Benchmark mit Go net/http

    • Verarbeitung von rund 2742 Requests pro Sekunde bei einer durchschnittlichen Response-Latenz von 5,83 ms
    • 100.000 POST-Requests wurden in 36,4 Sekunden verarbeitet
    • Der Durchsatz lag im Schnitt bei 2.742 RPS, die mittlere Latenz bei 5,8 ms und damit numerisch über Apache
    • Mehr als 95 % der Requests wurden innerhalb von 6 ms verarbeitet
    • CGI unter Go verfügt ebenfalls über ausreichend Performance für den Praxiseinsatz
  • Read-Benchmark mit Go net/http

    • Verarbeitung von rund 2469 Requests pro Sekunde bei einer durchschnittlichen Response-Latenz von 6,47 ms
    • 100.000 GET-Requests wurden in 40,4 Sekunden verarbeitet
    • Die meisten Requests konnten innerhalb von 7 ms bedient werden
    • Sowohl Read-Durchsatz als auch Response-Geschwindigkeit sind ähnlich gut oder besser als bei Apache

Fazit und Link

  • CGI-Programme bieten auf aktueller Hardware Vorteile wie extrem schnelle Nebenläufigkeit, einfaches Deployment und automatische Ressourcenfreigabe durch das Betriebssystem
  • Im Vergleich zu modernen Frameworks sind sie äußerst simpel, können für Dienste einer bestimmten Größenordnung aber auch heute noch produktiv genutzt werden
  • Das Gästebuch-Beispiel und die Benchmark-Daten sind im folgenden GitHub-Repository veröffentlicht
    https://github.com/Jacob2161/cgi-bin

4 Kommentare

 
kansm 2025-07-09

Krass … Werden wir wirklich wieder CGI verwenden?? Haha
Wow … Aus welcher Zeit CGI noch stammt …

 
tujuc 2025-07-08

Es gibt offenbar ein Update vom 7.7.

Serving a half billion requests per day with Rust + CGI

500 Millionen Requests …

 
GN⁺ 2025-07-07
Hacker-News-Kommentare
  • Erinnerung an Umgebungen aus den 1990ern, in denen in C geschriebene CGI-Programme wirklich sehr schnell waren; zugleich wird anerkannt, dass die hohe Fehleranfälligkeit ein Nachteil war; moderne Sprachen wie die im Artikel erwähnten Go-Programme oder Nim wirken auf localhost extrem schnell und latenzarm, solange keine Datenbankverbindung nötig ist, fast wie fork & exec bei einem CLI-Utility; verglichen mit Netzwerklatenz waren die Kosten nahezu vernachlässigbar

    • Allerdings wird eine Kultur erwähnt, die leicht von bestimmten Technologien abhängig wird; wenn man sich etwa an Sprachen mit hohen Startkosten wie den Python-Interpreter gewöhnt, braucht man irgendwann ein Multi-Shot- oder persistentes Modell

    • Das One-Shot-Modell des frühen HTTP entstand ursprünglich aus dem Problem, dass FTP-Server nicht genug Speicher hatten, um Hunderte inaktive Login-Sitzungen lange offen zu halten

    • Es wird die Möglichkeit eines hervorragenden Systemdesigns erwähnt, wenn man bei CGI Pre-Forking (das Latenz verbergen kann) mit sicheren Sprachen wie Rust kombiniert; TLS-Terminierung kann bequem von einem multithreaded Webserver (oder einer Schicht wie CloudFront) übernommen werden

      • Ein zustandsloses Umfeld, in dem Core Dumps und Debugging sehr einfach sind, und das sich vor allem mit einem linearen Request-Modell auch leicht skalieren lässt
      • Es wird die Einfachheit gelobt, nur von stdin zu lesen und nach stdout zu schreiben; WebSockets erhöhen die Komplexität zwar etwas, aber nicht in besorgniserregendem Maß
      • Erinnerung daran, dass der Aufstieg von Java den schnellen Wechsel zu Application-Servern auslöste, um die Kosten von fork() und die Risiken von C zu vermeiden; nun könne man wieder zur Einfachheit zurückkehren
      • Auch wenn Rust nicht gemocht wird, gibt es die Hoffnung, dass ein Zeitalter, in dem sich Web-Backend-Code auf diese Weise leicht schreiben lässt, auch auf Node/JS-, PHP- und Python-Entwickler attraktiv wirken wird
  • Erfahrung, seit der CGI-Zeit zu entwickeln und dadurch eine starke Abneigung gegen kurzlebige Subprozesse entwickelt zu haben

    • Erläuterung des Hintergrunds, dass PHP und FastCGI geschaffen wurden, um dem Performance-Problem zu entkommen, für jeden Web-Request einen neuen Prozess zu erzeugen

    • Dank der Fortschritte der aktuellen Hardware wurde klar, dass die Kosten des Prozessstarts in der Praxis gar kein großes Problem mehr sind

    • Hinweis auf dieses Benchmark, das 2000 Requests pro Sekunde verarbeiten kann, und dass sich schon bei einigen Hundert Requests moderne Umgebungen leicht über mehrere Instanzen skalieren lassen

    • Zustimmung zu der Meinung, AWS Lambda sei eine Wiedergeburt des CGI-Modells; eine ziemlich treffende Analogie

    • Wären CGI-Skripte als statisch gelinkte C-Binaries bereitgestellt worden, mit Blick sogar auf die Dateigröße, wäre die Enttäuschung wohl geringer ausgefallen

      • Die Startkosten eines dynamisch gelinkten Prozesses durch Laden des PHP-Interpreters, diverser Libraries und Dateiparsing sind deutlich höher
      • Überzeugung, dass ein Ansatz mit Go schon vor 25 Jahren ausreichend konkurrenzfähig gewesen wäre
      • Hervorhebung, dass das Öffnen einer SQLite-Datenbank leistungsmäßig fast dem Weiterreichen eines Sockets per Kontextwechsel entspricht und deutlich schneller ist als ein entfernter MySQL-Zugriff
      • Behauptung, dass FastCGI auch für neue Anwendungen nach wie vor eine ausgezeichnete Wahl ist
    • CGI war in Umgebungen mit niedriger Last finanziell und leistungsmäßig keine große Belastung

      • Bei hoher Last sind dauerhaft laufende Prozesse wie bei FastCGI vorteilhafter
      • CGI kann zwar ebenfalls bis zu 2.000 rps verarbeiten, FastCGI erreicht aber deutlich höhere Leistung
      • Dass man nur einen zusätzlichen Serverprozess braucht und bei Upgrades lediglich neu starten muss, ist es wert, wenn Performance wichtig ist
    • Vor dem Erscheinen von Go war es in den 2000ern sowohl in puncto Sicherheit als auch Entwicklungsaufwand schwierig, CGI-Programme in C/C++ zu bauen

      • Perl und Python hatten beträchtliche Interpreter-Start- und Kompilierkosten, Java war praktisch noch langsamer
      • Zustimmung dazu, dass AWS Lambda einer Wiedergeburt des CGI-Modells nahekommt
      • Heute wirkt es, als sei man zu fast demselben Modell wie bei gemanagtem FastCGI zurückgekehrt
      • Bedauern über die Flut an Technologien, die unnötig viel Komplexität hinzufügen, obwohl es reichen sollte, einfach nur eine Binärdatei hochzuladen und auszuführen
  • Heute leben wir in einer Zeit, in der Server 384 CPU-Threads haben und selbst kleine VMs 16 CPUs besitzen können

    • Auf solcher Hardware kann man mit Kestrel problemlos Billionen Requests pro Tag verarbeiten

    • Eine ähnliche Entwicklungserfahrung wie mit PHP lässt sich über String-Interpolation-Operatoren bieten

    • Mit LINQ und String.Join() lassen sich HTML-Tabellen und verschachtelte Elemente einfach templatisieren

    • Die eigentliche Schwierigkeit besteht darin, zu wissen, wie man das Minenfeld des Ökosystems aus MVC/Blazor/EF geschickt umgeht

    • Man kann sogar das gesamte Programm als eine einzige Datei über die CLI ausführen, aber ohne das Stichwort "Minimal APIs" gerät man leicht in ein Labyrinth aus irreführender Dokumentation

      • Erstaunen darüber, wie viele Leute durch das Aufsetzen von Abstraktionsschichten auf Kerntechnologien zu Director-/VP-Positionen befördert werden
  • Ein Vorteil von CGI ist, dass man in Multi-Tenant-Umgebungen keine neuen Isolationsprimitiven aufbauen muss

    • Selbst wenn ein einzelner Request einen Bug hat, beeinflusst er dank Prozessisolation keine anderen Requests
    • Auch Endlosschleifen führen dank präemptivem Scheduling nicht gleich zu einem Denial-of-Service (DoS)
    • Mit rlimit lassen sich lang laufende Requests zwangsweise beenden
    • Mit cgroup lassen sich Speicher-, CPU- sowie Festplatten-/Netzwerk-I/O-Ressourcen fair pro Tenant zuweisen
    • Mit Namespaces/Jails und Rechte-Trennung lassen sich Zugriffsrechte pro Request einschränken
  • Dank CGI wurde Perl für schnelle Startzeiten optimiert

    • Beim Ausführen des Befehls time perl -e '' zeigt sich: Perl braucht 5 ms, python3 33 ms und Ruby 77 ms; daran sieht man die schnelle Startzeit von Perl

      • Erwähnung, dass Skripte im Stil #!/bin/tcc -run aus dem tcc-mob-Branch 1,3-mal schneller als Perl sind
      • Beispiele wie Julia, Java VM oder thread-PHP, bei denen die Startzeiten ebenfalls sehr lang werden
      • Das Phänomen, dass sich Menschen gewohnheitsmäßig auf "große Umgebungen" verlassen
      • Auch in der Lisp-Community wiederholt sich das durch die Verwendung von Images; das Meme "emacs is bloated" sei daraus entstanden
      • Die Blütezeit von Perl in den späten 1990ern sei wirklich erst durch CGI möglich geworden
      • Rückblick auf eine Zeit, in der selbst getline noch kein Standard war und man Third-Party-C-Libraries mit einigen Hundert bis einigen Tausend Zeilen schrieb
      • Letztlich werden Technologien nach "Ruf" ausgewählt, und die meisten lernen das, was Freunde ihnen empfehlen
  • Wer Apache Tomcat 11 verwendet, kann .jsp-Dateien oder ganze Java-Servlet-Anwendungen (.war) einfach per ssh hochladen, und sie funktionieren sofort

    • Maximale Performance durch eine gemeinsame JVM

    • Auch DB-Connection-Pools, Caches usw. können zwischen Anwendungen geteilt werden

    • Eine wirklich beeindruckende Erfahrung

      • Es hängt von den tatsächlichen Nutzungsmustern ab

      • Für große Services ist das hervorragend, aber wenn 50 kleine Anwendungen jeweils nur einige Hundert Requests pro Tag verarbeiten müssen, ist der Speicher-Overhead von Tomcat im Vergleich zu Apache/Nginx auf CGI-Skriptbasis viel zu groß

      • Nostalgische Bemerkung, dass die Zeit vermisst wird, in der Deployment nur daraus bestand, Dateien einfach zu kopieren

      • Bedauern darüber, warum Deployment-Prozesse so kompliziert geworden sind

      • Geteilte Erfahrung, dass Jetty auch heute noch mit Freude für Backend-Web-Apps genutzt wird

      • Eindruck, dass der Tomcat/Jakarta-EE/JSP-Stack überraschend robust ist

      • Man kann wie bei PHP HTML und Code vermischen, aber auch reine Java-Routen verwenden

      • Unterstützung für WebSockets, und durch das Single-Process-/Multithread-Modell auch stark für Echtzeitkommunikation

      • Bei Bedarf können Daten zwischen Requests geteilt werden; JSP-Code ist standardmäßig auf den Request-Scope beschränkt

      • Deployment ist wirklich einfach: Sobald neue Dateien in das Verzeichnis webapps hochgeladen werden, lädt Tomcat die neue App automatisch und entlädt die bestehende

      • Ein Nachteil ist, dass Lecks im Classloader dazu führen können, dass Garbage Collection fehlschlägt, ein Schicksal des Single-Process-Modells

  • Erstellung des Visualisierungstools für Apache-Requests ibrahimdiallo.com/reqvis

    • Die beste Erfahrung bietet es im Desktop-Browser
    • Auf Basis von HN-Traffic-Daten lässt sich der tatsächliche Ablauf im Web nachvollziehen
  • Zweifel daran, dass man heute in immer komplexere Architekturen geht; möglicherweise könnte man mit guter Hardware auch bestehende Technik weiterhin ausreichend nutzen

    • Bei der Frage nach dem Design eines Systems, das Millionen Menschen in Echtzeit über Aktienkurse informiert, dachte man zunächst an komplexe Stream-Strukturen wie Kafka oder Pub/Sub, erwog am Ende aber auch die einfache Methode, statische Dateien auf dem Server abzulegen

    • Neugier auf die tatsächlichen Betriebskosten eines solchen Ansatzes

      • Praktisch wird die Latenz fast jeder Web-API durch DB-Queries oder ML-Modellabfragen bestimmt
      • Der restliche Prozess ist selbst mit langsameren Sprachen wie Python kaum der Rede wert
      • Wenn nur selten wechselnde Daten zurückgegeben werden, lässt sich die NIC-Grenze leicht erreichen
  • Hervorgehoben wird, dass es einer serverless Architektur ähnelt, aber viel einfacher und günstiger ist

    • Frage, ob es in der Praxis im Business-Umfeld reale Beispiele für diese Nutzungsweise gibt
  • Bedauern darüber, dass man statt einer Neubewertung solcher traditionellen Strukturen einfach nur das neue Paradigma der "serverless functions" geschaffen hat

    • Serverless Functions wie Lambda haben zwar zusätzliche Schutzmechanismen wie Micro-VMs, aber man hätte mit CGI und Rechte-Steuerung bei deutlich geringerer Komplexität vermutlich sehr viel weiter kommen können
 
regentag 2025-07-07

Na gut, CGI ist das eine, aber die Reaktion auf JSP überrascht mich echt, haha.
Ist JSP etwa schon zu so einem antiken Relikt geworden?