- Se comprobó mediante experimentos que los programas CGI, ampliamente usados en la primera era de la web, todavía pueden ofrecer alto rendimiento en hardware moderno
- CGI procesa las solicitudes por proceso, lo que permite gestión automática de memoria y la ventaja de un despliegue simple
- Los resultados del benchmark demostraron que incluso en un servidor común con CPU de 16 hilos es posible procesar más de 2,400 requests por segundo, o más de 200 millones al día
- Se publicó como open source un ejemplo de guestbook.cgi escrito en Go y SQLite, junto con su Dockerfile
- Aunque CGI ya no es de uso común hoy en día, muestra que todavía puede ser una alternativa práctica y moderna
Programas CGI y cómo funcionan
- A inicios de los 2000, los programas CGI (Common Gateway Interface) eran una de las principales formas de construir sitios web dinámicos
- La mayoría estaban escritos en Perl o C, y a veces se elegía C para mejorar el rendimiento
- El concepto de CGI es simple pero potente
- El servidor web configura en variables de entorno los metadatos de la solicitud (headers HTTP, query, etc.)
- Crea un proceso separado para ejecutar el programa CGI
- Envía el cuerpo de la solicitud por stdin
- Captura el stdout del programa como respuesta HTTP
- Envía la salida de stderr al log de errores del servidor
- Cuando el programa termina de atender la solicitud, el proceso finaliza y los file descriptors y la memoria se liberan automáticamente
- Desde la perspectiva del desarrollador, desplegar una nueva versión también era muy simple: bastaba con copiar el archivo al directorio
cgi-bin/
Hug of death (avalancha de tráfico)
- A inicios de los 2000, la mayoría de los servidores web solían tener 1 o 2 CPU y entre 1 y 4 GB de memoria
- Debido a que el servidor web Apache usaba una estructura en la que hacía fork de un proceso
httpd por cada conexión, los requisitos de memoria aumentaban cuando había muchas conexiones
- Era difícil superar las 100 conexiones simultáneas, y bastaba con que un sitio famoso pusiera un enlace para que el servidor se sobrecargara fácilmente
- ( Slashdot Effect: si aparecía un enlace en Slashdot, que era muy popular en esa época, llegaba una avalancha de tráfico. Algo parecido a subir hoy a la portada de Hacker News)
CGI en entornos de servidores modernos
- Hoy existen servidores con 384 hilos de CPU, y hasta una VM relativamente pequeña puede ofrecer 16 CPU
- El rendimiento de CPU y memoria ha mejorado enormemente
- Como los programas CGI se basan en procesos separados, pueden aprovechar de forma natural los multicore
- Por eso se realizó un benchmark para medir directamente qué tan rápido puede ser CGI en hardware moderno
- El experimento se ejecutó en un servidor con AMD 3700X (16 hilos)
Principales resultados del benchmark
- Se probó un programa CGI simple tanto en un entorno con Apache como con un servidor Go
net/http
-
Descripción del programa guestbook.cgi
- Con la herramienta de generación de carga HTTP
plow se ejecutaron 100 mil requests con 16 conexiones
- Incluso en hardware común, es posible procesar más de 2,400 requests por segundo, es decir, más de 200 millones de requests al día
- CGI no es la corriente principal hoy, pero todavía puede usarse en operación real de servicios
-
Benchmark de escritura en entorno Apache
- Se procesaron aprox. 2468 requests por segundo, con una latencia promedio de 6.47 ms
- 100 mil requests POST se procesaron en solo 40.5 segundos
- La mayoría de las solicitudes respondieron en menos de 7 ms, y solo una fracción muy pequeña superó los 100 ms
- Esto demuestra en la práctica un alto rendimiento de escritura
-
Benchmark de lectura en entorno Apache
- Se procesaron aprox. 1959 requests por segundo, con una latencia promedio de 8.16 ms
- 100 mil requests GET se procesaron en 51 segundos
- Más de la mitad de las solicitudes respondieron en menos de 8 ms, y la latencia máxima fue de apenas 31 ms
- El rendimiento de lectura también es suficientemente bueno
-
Benchmark de escritura en entorno Go net/http
- Se procesaron aprox. 2742 requests por segundo, con una latencia promedio de 5.83 ms
- 100 mil requests POST se procesaron en 36.4 segundos
- El throughput fue en promedio de 2,742 RPS, con latencia media de 5.8 ms, mostrando mejores cifras que Apache
- Más del 95% de las solicitudes se procesaron en menos de 6 ms
- CGI en entorno Go también ofrece rendimiento suficiente para producción
-
Benchmark de lectura en entorno Go net/http
- Se procesaron aprox. 2469 requests por segundo, con una latencia promedio de 6.47 ms
- 100 mil requests GET se procesaron en 40.4 segundos
- La mayoría de las solicitudes pudieron atenderse en menos de 7 ms
- Tanto el throughput de lectura como la velocidad de respuesta son similares o superiores a los de Apache
Conclusión y enlaces
- Los programas CGI tienen ventajas como concurrencia ultrarrápida en hardware moderno, despliegue simple y liberación automática de recursos por parte del sistema operativo
- Aunque son extremadamente simples frente a frameworks modernos, todavía pueden usarse en producción para servicios de cierto tamaño
- El ejemplo del libro de visitas y los datos del benchmark están publicados en el siguiente GitHub
https://github.com/Jacob2161/cgi-bin
4 comentarios
Vaya... ¿vamos a volver a usar CGI?? jaja
Guau... de qué época será eso de CGI...
Parece que hay contenido actualizado al 7/7.
Serving a half billion requests per day with Rust + CGI
Nada menos que 500 millones de requests...
Comentarios en Hacker News
Recuerdo que incluso en los años 90 los programas CGI escritos en C mostraban una velocidad realmente alta, aunque admito que su desventaja era que fallaban mucho; programas modernos como los de Go o lenguajes como Nim, mientras no hagan conexión a base de datos, se sienten muy rápidos y con muy poca latencia en localhost, casi como usar
fork & execen una utilidad CLI; comparado con la latencia de red, el costo era casi despreciableTambién se menciona una cultura propensa a volverse adicta a cierta tecnología; por ejemplo, una vez que te acostumbras a lenguajes con alto costo de arranque como el intérprete de Python, terminas necesitando un modelo multishot o persistente
El modelo one-shot del HTTP temprano surgió del problema de que no había suficiente memoria para que un servidor FTP mantuviera durante mucho tiempo cientos de sesiones de login inactivas
Se menciona que combinar pre-forking en CGI (que puede ocultar la latencia) con un lenguaje seguro como Rust permitiría diseños de sistema sobresalientes; además, la terminación TLS puede manejarse en un servidor web multihilo (o en una capa como CloudFront), lo que lo vuelve muy conveniente
fork()como los riesgos de C; ahora se argumenta que ya podemos volver a la simplicidadHaber empezado a desarrollar desde la época de CGI le dejó una fuerte aversión a ejecutar subprocesos de vida corta
Explica que PHP y FastCGI surgieron para escapar del problema de rendimiento de crear un proceso nuevo por cada petición web
Gracias al avance reciente del hardware, se dio cuenta de que el costo de iniciar procesos en realidad ya no es un gran problema
Menciona que este benchmark puede manejar 2000 solicitudes por segundo, y que incluso con solo unas pocas centenas ya es fácil escalar con varias instancias en un entorno moderno
Coincide con la opinión de describir AWS Lambda como un renacimiento del modelo CGI; le parece una analogía bastante acertada
Comenta que, si los scripts CGI se hubieran desplegado como binarios C con enlace estático y cuidando incluso el tamaño, probablemente la decepción habría sido menor
CGI no era una carga grande ni en costo ni en rendimiento en entornos de baja carga
Antes de que apareciera Go, en los 2000 hacer programas CGI en C/C++ implicaba grandes riesgos y alta dificultad de desarrollo
Hoy vivimos en una época en la que un servidor puede tener 384 hilos de CPU y hasta una VM pequeña puede tener 16 CPUs
En hardware así, si desarrollas con Kestrel, puedes manejar sin problema hasta billones de solicitudes al día
Se puede ofrecer una experiencia de desarrollo similar a PHP usando operadores de interpolación de strings
Con LINQ y
String.Join()es fácil plantillar tablas HTML y elementos anidadosLa verdadera dificultad es saber cómo esquivar bien el campo minado del ecosistema de MVC/Blazor/EF
También es posible ejecutar todo el programa como un solo archivo de nivel superior desde la CLI, pero si no conoces la palabra clave "Minimal APIs", es fácil perderse en un laberinto de documentación equivocada
La ventaja de CGI es que en un entorno multitenant no hace falta reconstruir primitivas de aislamiento desde cero
rlimites posible terminar a la fuerza solicitudes que tardan demasiadocgroupse puede asignar de forma justa por tenant el uso de memoria, CPU y disco/IO de redGracias a los scripts CGI, perl fue optimizado para tener tiempos de arranque rápidos
Al ejecutar el comando
time perl -e '', perl tarda 5ms, python3 33ms y ruby 77ms, lo que confirma el rápido tiempo de arranque de perl#!/bin/tcc -runde la rama mob de tcc son 1.3 veces más rápidos que perlgetlineera estándar, y se hacían bibliotecas C de terceros de cientos o miles de líneasSi pruebas apache tomcat 11, basta con subir por ssh un archivo
.jspo toda una aplicación java servlet (.war) y simplemente funcionaSe obtiene el máximo rendimiento con una JVM compartida
El pool de conexiones DB, cachés y demás también pueden compartirse entre aplicaciones
Es una experiencia realmente impresionante
Depende del patrón real de uso
Para servicios grandes es excelente, pero si tienes 50 aplicaciones pequeñas que solo deben procesar unos cientos de casos al día cada una, se señala que el overhead de memoria de Tomcat es demasiado grande frente a Apache/Nginx basado en scripts CGI
Expresa nostalgia por la época en que desplegar era simplemente copiar archivos
Lamenta que el proceso de despliegue se haya vuelto tan complicado
Comparte que incluso hoy sigue usando Jetty con gusto para aplicaciones web backend
Le parece que el stack Tomcat/Jakarta EE/JSP es sorprendentemente bastante sólido
Permite mezclar HTML y código como en PHP, y también usar rutas en Java puro
Tiene soporte para WebSockets, y al ser un modelo multihilo de proceso único también tiene ventajas para comunicación en tiempo real
Si hace falta, se pueden compartir datos entre solicitudes, y el código JSP por defecto queda limitado al alcance de la solicitud
El despliegue es realmente fácil: basta con subir un archivo nuevo al directorio
webappsy Tomcat carga automáticamente la nueva app y descarga la anteriorComo desventaja, menciona que puede haber fugas del classloader que impidan al garbage collection limpiar bien, algo propio del modelo de proceso único
Se creó una herramienta de visualización para solicitudes de apache: ibrahimdiallo.com/reqvis
Últimamente le parecía sospechoso que estuviéramos yendo hacia arquitecturas tan complejas, y menciona la posibilidad de que en realidad todavía podamos usar tecnologías anteriores si contamos con buen hardware
Ante la pregunta de cómo diseñar un sistema para informar precios de acciones en tiempo real a millones de personas, primero pensó en estructuras complejas de streaming como Kafka o pubsub, pero al final también consideró el enfoque simple de dejar archivos estáticos en el servidor
Le da curiosidad el costo real de operar algo así
Se enfatiza que es parecido a una arquitectura serverless, pero mucho más simple y barata
Lamenta que no se haya replanteado esta estructura tradicional y que en cambio solo se haya creado un nuevo paradigma llamado "serverless functions"
Bueno, lo de cgi pase, pero me sorprende la reacción sobre jsp jaja
¿jsp ya se convirtió en una reliquia tan antigua?