2 puntos por GN⁺ 2025-07-07 | 1 comentarios | Compartir por WhatsApp
  • 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

 
GN⁺ 2025-07-07
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 cgi fue eliminado en la versión 3.13
    Llevo 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 cgi es PEP 594 cgi
      Ese enlace lleva a PEP 206, escrito en el año 2000 (hace 25 años), donde ya se explicaba que "el paquete cgi tiene 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 cgi
      La implementación de scripts CGI sigue siendo compatible a través de CGIHTTPRequestHandler en el módulo http.server
      También señalan que el módulo cgi originalmente solo incluía unas cuantas funciones para parsear datos de formularios HTML

    • Entiendo criticar que el módulo cgi salga de la biblioteca estándar de Python, pero JS, que suele mencionarse como alternativa, ni siquiera tiene biblioteca estándar como tal
      Tambié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-bin necesitas acceso a la base de datos, está la incomodidad de crear una conexión nueva en cada proceso
    Si 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í, inetd es CGI en su forma más pura
    Gracias a eso, Internet se sentía mucho más divertido
    Fue una época en la que llegué a correr con inetd varios scripts de shell, e incluso HTTP escrito solo en Bash
    Ya 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 usando netcat y grep
    Realmente 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-bin para levantar rápido webapps internas sencillas
    Si 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 usar wsgiref.handlers.CGIHandler de Python para ejecutar cualquier app WSGI como script CGI
    El ejemplo con Flask también es así de simple

     import wsgiref.handlers, flask
     app = flask.Flask(__name__)
     wsgiref.handlers.CGIHandler().run(app)
    

    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_cgi en Apache o lighttpd
    Como 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

     cgi = /cgi-bin=/webapps/cgi-bin/src
     cgi-allowed-ext = .py
     cgi-helper = .py=/webapps/cgi-bin/venv/bin/python3 # all dependencies go here
    

    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

    • Buen tip
      Es útil saber que wsgiref.handlers.CGIHandler todavía no está deprecated
  • Hilo relacionado discutido ayer enlace

  • Hace poco, al usar Apache para un side project, me resultó útil la función .htaccess
    Puedes dejar un archivo .htaccess en cualquier directorio y cargar configuración adicional del servidor en cada solicitud
    Documentación oficial de htaccess
    Antes se recomendaba evitar .htaccess por 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 posible
    Pero 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 import se vuelve cuello de botella
    El 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

    • En 1995, Amazon usaba ejecutables C++ como CGI
      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