- Incluso con programación CGI es posible procesar más de 200 millones de solicitudes web al día
- Las recientes mejoras en el rendimiento del hardware han reducido enormemente las desventajas del enfoque CGI
- Un programa CGI construido con Go y SQLite mostró un rendimiento sobresaliente en una CPU de 16 hilos
- CGI ofrece una arquitectura especialmente adecuada para aprovechar varios núcleos de CPU
- Gracias a la tecnología moderna, las formas de desarrollar aplicaciones web del pasado vuelven a mostrarse suficientemente prácticas
El pasado y el presente de CGI
- A finales de los años 90, el autor empezó a desarrollar para la web con CGI y en ese entonces usaba sistemas como NewsPro
- CGI genera mucho overhead porque para cada solicitud web lanza y termina un proceso nuevo
- Por esa razón se desarrollaron tecnologías alternativas más eficientes como PHP y FastCGI
Avances en el rendimiento del hardware
- Durante los últimos más de 20 años, la velocidad y capacidad de las computadoras aumentó de forma drástica
- En 2020, el autor redescubrió la viabilidad práctica del modelo basado en ejecución de procesos al usar herramientas hechas en Go y Rust, como ripgrep
Ventajas del enfoque CGI moderno
- Si CGI se implementa con lenguajes de ejecución rápida como Go y Rust, se eliminan la mayoría de las desventajas del CGI tradicional
- Los programas CGI funcionan como procesos separados por solicitud, lo que los vuelve ideales para aprovechar CPU multinúcleo
- Por ejemplo, en un entorno de 16 hilos se comprobó la posibilidad de manejar más de 2400 solicitudes por segundo, es decir, más de 200 millones de solicitudes al día
- Los servidores grandes pueden ofrecer 384 hilos de CPU o más
Ideas sobre la cultura de desarrollo
- Con la adopción actual de lenguajes como Go y Rust, el enfoque CGI de los años 90 puede volver a tener sentido
- Aun así, no sigue siendo una solución adecuada para todos los entornos y no se recomienda como enfoque principal
- Lo importante es que, en el contexto actual, se demuestra experimentalmente que CGI ya no es una solución tan ineficiente como antes
Conclusión
- Con hardware moderno y soporte de lenguajes rápidos, la programación CGI muestra un rendimiento imposible de comparar con el del pasado
- Como ejemplo de cómo aprovechar al máximo las ventajas del multiproceso, ofrece una reflexión interesante para desarrolladores web
1 comentarios
Opiniones de Hacker News
Incluso hoy, con Python, CGI se siente bastante rápido
Aunque un script CGI use 400 milisegundos de CPU al arrancar, si el servidor tiene 64 núcleos puede procesar 160 solicitudes por segundo, o manejar 14 millones de solicitudes al día por servidor
Eso significa que incluso un tráfico diario de cientos de millones de solicitudes (sin contar recursos estáticos) no tiene necesariamente al arranque de procesos CGI como cuello de botella
Antes pensaba que esta clase de tecnología era "tan estable que resultaba aburrida", y por eso siempre estaba en la biblioteca estándar de Python, pero los mantenedores actuales de Python más bien tienen una postura negativa frente a la estabilidad y la compatibilidad hacia atrás
Por eso están quitando de la biblioteca estándar módulos demasiado "aburridos y estables", y de hecho el módulo
cgifue eliminado en la versión 3.13Llevo casi 25 años usando Python para prototipado por costumbre, pero ahora lo lamento
Estoy entre JS y Lua sin decidirme
El enlace con la explicación oficial sobre la eliminación de
cgies PEP 594 cgiEse enlace lleva a PEP 206, escrito en el año 2000 (hace 25 años), donde ya se explicaba que "el paquete
cgitiene un diseño deficiente y es difícil de tocar"En el repositorio jackrosenthal/legacy-cgi se puede ver un reemplazo drop-in del módulo de la biblioteca estándar
Los desarrolladores de Python solo quitaron el módulo llamado
cgiLa implementación de scripts CGI sigue siendo compatible a través de
CGIHTTPRequestHandleren el módulohttp.serverTambién señalan que el módulo
cgioriginalmente solo incluía unas cuantas funciones para parsear datos de formularios HTMLEntiendo criticar que el módulo
cgisalga de la biblioteca estándar de Python, pero JS, que suele mencionarse como alternativa, ni siquiera tiene biblioteca estándar como talTambién señalan que Lua tampoco tiene un módulo CGI en su stdlib
Personalmente prefiero PHP o JS
En estos casos es cómodo que la caja ya venga con un JIT por defecto
Uso Python desde la versión 1.6, pero casi siempre solo para scripting del sistema operativo
Antes tuve la experiencia de integrar Tcl con módulos de Apache o IIS y terminar reescribiendo módulos una y otra vez en C (1999~2003)
Si un script CGI consume 400 milisegundos de CPU, la latencia de respuesta de ese endpoint también será como mínimo de ese nivel, así que sí afecta la usabilidad
Hace poco monté en un mini servidor de 350 dólares un binario de golang, rabbitmq, redis y MySQL, y en ese mismo servidor maneja de forma sostenida 5,000 req/s
En 24 horas eso da capacidad para 400 millones de solicitudes
Realmente se nota lo buenos que son hoy las herramientas gratuitas
Aun así, sigo pensando que la nube cuesta demasiado
Claro, no es una comparación 1:1, pero fue muy satisfactorio poder desarrollar y tunear todo yo mismo en un servidor en el sótano de mi casa
Hay casos en los que usar un bloque de microservicios sobre Kubernetes hace que el desarrollo sea 10 veces más lento
Mucha gente no entiende que un servidor no es una máquina que solo procesa 1 solicitud por segundo
La realidad es que se paga un overhead excesivo solo porque "si Google lo hace, yo también"
Yo mismo pienso que debería escribir algo sobre la arquitectura de "monolito modular", que en nuestro equipo funciona muy bien
Quise hospedar side projects en casa, pero hay muchos riesgos: cortes de energía, caídas del ISP, falta de acceso remoto, fallas del disco duro, etc.
Al final, si también cuentas el valor de tu tiempo, el beneficio económico ya no es tan claro
Los servicios en la nube sí aprovechan economías de escala, así que en la práctica pueden ser una opción razonable
Ni siquiera hace falta irse a la nube; también se puede rentar un servidor dedicado con un proveedor de hosting
Claro, ahí sí hay límites de ancho de banda y tráfico
Si la nube es dominante, es porque los VC o inversionistas tienen participación en esas empresas, o por el miedo de que "de pronto llegue tráfico infinito"
Los expertos de ventas cloud saben explotar muy bien la ansiedad de los inversionistas
Tampoco es que todo el mundo use nube sí o sí
En servicios reales, el costo alto de las VM no suele venir del cómputo de alto rendimiento, sino de necesitar cantidades enormes de disco local
No hace falta tanta capacidad de cómputo; con 4 discos de 20 TB y un CPU decente ya se puede imaginar un servicio muy potente
En la nube es casi imposible encontrar una combinación así
Si en
cgi-binnecesitas acceso a la base de datos, está la incomodidad de crear una conexión nueva en cada procesoSi el código se ejecuta en memoria, como con fastcgi, no solo se reduce el tiempo de arranque, sino que también puedes tener pool de conexiones o conexiones persistentes por hilo
A gran escala, el número de conexiones a la base de datos crece demasiado y termina ahogándola
Se operan muchos procesos por ideas como "Python es single-threaded, así que varios procesos" y "Python es lento, así que más procesos"
Al final terminas separando un shared connection pool fuera de los procesos de Python, con algo como pg bouncer, y hace falta bastante tuning
En mi caso, al final lo reimplementé en un lenguaje más manejable, con soporte multihilo y mejor rendimiento, y todo quedó mucho más simple
Por eso CGI terminó evolucionando hacia modelos que conservan información entre solicitudes, como fastcgi
Tradicionalmente también se levantaba un daemon independiente para que hiciera de proxy, y si usabas sockets Unix la conexión era mucho más eficiente que con TCP/IP
Alguien sugiere usar UDP
Para mí,
inetdes CGI en su forma más puraGracias a eso, Internet se sentía mucho más divertido
Fue una época en la que llegué a correr con
inetdvarios scripts de shell, e incluso HTTP escrito solo en BashYa desaparecieron esos VPS viejos y las laptops sin backups ni control de versiones, pero quedan buenos recuerdos
El despliegue también era simple, con Makefile +
scp, y las pruebas se podían hacer con scripts Bash escritos usandonetcatygrepRealmente se siente que vivimos en una gran época
Me parece que lograr 2400 rps con una app hello world no es nada impresionante para el hardware actual
El código tampoco se volvió más simple, así que queda la duda de para qué estamos sacrificando rendimiento
Si no necesitas procesar más de 2000 rps, entonces no hay problema
La idea es que son muy pocos los sitios que realmente requieren ese nivel de tráfico
En números no parece muy alto, pero en la práctica basta para muchísimos entornos
Incluso aguantaría el "hug of death" del que hablan los desarrolladores de HN, o sea, esos picos repentinos de tráfico
En nuestra empresa todavía usamos el directorio
cgi-binpara levantar rápido webapps internas sencillasSi se usa de forma simple, la eficiencia de desarrollo es muy alta
Incluso con CGI no hace falta imprimir directamente
http/1.0, y se puede usarwsgiref.handlers.CGIHandlerde Python para ejecutar cualquier app WSGI como script CGIEl ejemplo con Flask también es así de simple
En producción ejecutamos scripts con el plugin CGI de uwsgi
Da la impresión de que es mucho más simple y flexible que correr
mod_cgien Apache o lighttpdComo uwsgi se ejecuta como unidad del sistema, también puedes aprovechar todas las funciones de hardening y sandboxing de systemd
Además, en el manejo CGI de uwsgi se puede especificar un intérprete por tipo de archivo
El tiempo hasta el primer byte está entre 250 y 350 ms, y para nuestro uso es totalmente aceptable
Documentación de uwsgi sobre CGI
Es útil saber que
wsgiref.handlers.CGIHandlertodavía no está deprecatedHilo relacionado discutido ayer enlace
Hace poco, al usar Apache para un side project, me resultó útil la función
.htaccessPuedes dejar un archivo
.htaccessen cualquier directorio y cargar configuración adicional del servidor en cada solicitudDocumentación oficial de htaccess
Antes se recomendaba evitar
.htaccesspor rendimiento, debido al overhead de acceder al disco en cada solicitud, y se sugería integrar todo en la configuración principal siempre que fuera posiblePero hoy, con SSD y suficiente RAM, aunque claro que hay una pequeña pérdida de rendimiento, los CPU ya son lo bastante buenos como para que en la mayoría de los casos se pueda ignorar
[Mi proyecto StaticPatch][https://github.com/StaticPatch/StaticPatch/tree/main] ya lo usa así
Una cita célebre de Rasmus Lerdorf, creador de PHP
"Yo no soy realmente un programador; solo hago que funcione y sigo adelante. Un programador de verdad diría: 'hay una fuga de memoria, hay que arreglarla'. Yo solo reinicio Apache cada 10 solicitudes"
PHP luego recorrió un largo camino, superó muchos de sus errores iniciales y evolucionó bastante
También se cuenta que dijo: "PHP 8 me gusta más mientras menos código mío tenga"
No entiendo por qué Apache no vigila el sistema de archivos y relee solo cuando hay cambios, en vez de forzar accesos innecesarios al disco en cada solicitud
El resultado, dicen, es que el 99.99% de las solicitudes HTTP terminan siendo más lentas
Últimamente he estado pensando en una estructura así para prototipado rápido dentro de mi workflow
En lenguajes con JIT, si no se usa algo tipo fastcgi, el
importse vuelve cuello de botellaEl servidor web h2o que llegué a usar tenía configuración simple para handlers de mruby y fast-cgi, y era ideal para trabajo con scripts locales
Documentación de fastcgi en h2o
Otra ventaja es que también sirve cuando quieres permitir que el cliente agregue código personalizado para extender software local
Por ejemplo, antes había que usar MCP para extensiones, pero ahora basta con implementar solicitudes estructuradas mediante CGI
Como entorno para usuario final, también podría ser interesante conectar programas CGI al frente de MCP
Un servicio MCP perfectamente podría implementarse como CGI
Siento que tendría que revisar mejor la especificación
Queda la duda de si fastcgi no termina perdiendo casi todas las ventajas que tenía cgi
Antes usé directamente programas en C combinados con CGI
En ese tiempo no había más de 100 núcleos ni RAM abundante, y todo corría incluso con un máximo de 1 GB de memoria
Viendo lo que ya era posible entonces, estoy seguro de que hoy sería mucho más fácil
Cuando hizo falta balanceo de carga, la escalabilidad se complicó, pero antes de eso funcionó bastante bien
Por cierto, el frontend y el backoffice eran dos ejecutables separados