2 pontos por GN⁺ 2025-07-07 | 1 comentários | Compartilhar no WhatsApp
  • Mesmo com programação em CGI, é possível processar mais de 200 milhões de requisições web por dia
  • Com a melhora recente no desempenho de hardware, as desvantagens do modelo CGI diminuíram bastante
  • Um programa CGI usando Go e SQLite mostrou desempenho excelente em uma CPU de 16 threads
  • O CGI oferece uma estrutura especialmente adequada para aproveitar vários núcleos de CPU
  • Com a tecnologia moderna, até formas antigas de desenvolver aplicações web podem ser totalmente viáveis na prática

O passado e o presente do CGI

  • No fim dos anos 1990, o autor começou a desenvolver para web com CGI e, na época, usava sistemas como o NewsPro
  • O CGI gerava alto overhead porque, a cada requisição web, repetia a execução e o encerramento de um novo processo
  • Por esse motivo, foram desenvolvidas tecnologias alternativas mais eficientes, como PHP e FastCGI

Evolução do desempenho de hardware

  • Ao longo dos últimos mais de 20 anos, a velocidade e o desempenho dos computadores aumentaram drasticamente
  • Em 2020, o autor redescobriu a praticidade da execução por processo ao usar ferramentas feitas em Go e Rust, como o ripgrep

Vantagens do CGI moderno

  • Ao implementar CGI com linguagens de execução rápida como Go e Rust, a maioria das desvantagens do CGI antigo desaparece
  • Programas CGI funcionam como processos separados por requisição, o que os torna ideais para aproveitar CPUs multicore
    • Por exemplo, em um ambiente com 16 threads, foi confirmada a possibilidade de processar mais de 2.400 requisições por segundo = mais de 200 milhões de requisições por dia
    • Servidores de grande porte podem oferecer mais de 384 threads de CPU

Insights sobre cultura de desenvolvimento

  • Hoje, com a adoção de linguagens como Go e Rust, o modelo de CGI dos anos 1990 pode voltar a fazer sentido
  • Ainda assim, ele não é adequado para todos os ambientes e não é recomendado como abordagem principal
  • O ponto importante é que, no momento atual, foi demonstrado experimentalmente que o CGI não é mais uma solução tão ineficiente quanto antes

Conclusão

  • Com hardware moderno e suporte de linguagens rápidas, a programação em CGI mostra um desempenho incomparável ao do passado
  • Como um caso que aproveita ao máximo as vantagens do multiprocesso, isso oferece reflexões interessantes para desenvolvedores web

1 comentários

 
GN⁺ 2025-07-07
Comentários no Hacker News
  • Hoje em dia, mesmo em Python, CGI parece ter um desempenho bem rápido
    Mesmo que um script CGI use 400 ms de CPU na inicialização, se o servidor tiver 64 núcleos, dá para processar 160 requisições por segundo, ou cerca de 14 milhões de acessos por dia por servidor
    Ou seja, mesmo tráfego diário na casa das centenas de milhões (excluindo assets estáticos) não significa que o startup do processo CGI seja o gargalo
    Antes eu pensava que esse tipo de tecnologia era "estável a ponto de ser entediante", então sempre estaria na biblioteca padrão do Python, mas hoje os mantenedores do Python parecem até ter uma postura negativa em relação à estabilidade e à compatibilidade retroativa
    Por isso, módulos "estáveis e entediantes" demais estão sendo removidos da biblioteca padrão, e de fato o módulo cgi foi apagado na versão 3.13
    É hábito de quase 25 anos usando Python para prototipagem, mas agora estou começando a me arrepender
    Estou dividido entre JS e Lua

    • O link da explicação oficial sobre a remoção do cgi é PEP 594 cgi
      Esse link leva ao PEP 206, escrito em 2000 (há 25 anos), onde já se dizia que "o pacote cgi tem um design ruim e é difícil de mexer"
      No repositório jackrosenthal/legacy-cgi dá para ver um drop-in replacement que substitui o módulo da biblioteca padrão praticamente sem mudanças

    • Os desenvolvedores de Python só removeram o módulo chamado cgi
      A implementação de scripts CGI continua sendo suportada em CGIHTTPRequestHandler, do módulo http.server
      Vale lembrar que o módulo cgi originalmente só tinha algumas funções para fazer parsing de dados de formulários HTML

    • Entendo a crítica ao fato de o módulo cgi estar saindo da biblioteca padrão do Python, mas o JS que costuma ser citado como alternativa nem sequer tem biblioteca padrão de verdade
      E o Lua também não tem módulo CGI na stdlib

    • Pessoalmente, prefiro PHP ou JS
      Nesses casos, ter JIT disponível por padrão já traz conveniência
      Uso Python desde a versão 1.6, mas principalmente para scripting de sistema
      Antigamente eu integrava Tcl como módulo do Apache ou do IIS e acabava sempre reescrevendo módulos em C de novo e de novo (1999~2003)

    • Se um script CGI usa 400 ms de CPU, então o tempo de resposta desse endpoint também passa a ser no mínimo isso, o que afeta a usabilidade

  • Recentemente subi um binário em golang, rabbitmq, redis e MySQL em um mini servidor de 350 dólares, e ele sustentou 5.000 req/s na mesma máquina
    Em 24 horas, isso dá capacidade para 400 milhões de requisições
    Dá para sentir como as ferramentas gratuitas de hoje são realmente excelentes
    Ainda assim, acho o custo de cloud alto demais
    Claro que não é uma comparação 1:1, mas foi muito satisfatório poder desenvolver e fazer tuning diretamente em um servidor no porão de casa

    • Existem casos em que usar um amontoado de microsserviços baseados em Kubernetes deixa o desenvolvimento 10 vezes mais lento
      Muita gente parece não perceber que servidor não é uma máquina que só processa 1 requisição por segundo
      Existe uma realidade em que se paga overhead demais só porque o Google faz assim
      Eu mesmo estou pensando em escrever um texto sobre arquitetura de "monólito modular", que funciona bem para o nosso time

    • Já tentei hospedar projetos paralelos em casa, mas os riscos são grandes: queda de energia, indisponibilidade do ISP, falta de acesso remoto, falha de disco etc.
      No fim, quando considero também o valor do meu tempo, o ganho econômico fica duvidoso
      Serviços de cloud se beneficiam de economia de escala, então na prática acabam sendo uma escolha razoável

    • Nem precisa ser cloud; também dá para alugar servidor dedicado de um provedor de hospedagem
      Claro, existem limites de banda/tráfego
      O motivo de a cloud ser dominante tem a ver com VC, investidores que têm participação nessas empresas ou o medo de que "o tráfego exploda sem limite"
      Profissionais de vendas de cloud exploram muito bem a ansiedade dos investidores

    • Não é como se todo mundo usasse cloud obrigatoriamente

    • Em serviços reais, o custo alto de VM muitas vezes não vem de computação de alto desempenho, mas da necessidade de uma quantidade enorme de disco local
      Não é preciso tanto poder de cálculo; com 4 discos de 20 TB e uma CPU razoável já dá para imaginar um serviço impressionante
      Na cloud, é quase impossível encontrar esse tipo de combinação

  • Quando o cgi-bin precisa acessar um banco de dados, é incômodo ter de criar uma nova conexão a cada processo
    Se o código roda em memória, como no fastcgi, você não só reduz o tempo de startup como também pode manter um pool de conexões com o banco ou conexões persistentes por thread

    • Em escala, o número de conexões com o banco cresce demais e o banco começa a sofrer
      Com argumentos como "Python é single-threaded, então precisamos de vários processos" e "Python é lento, então precisamos de ainda mais processos", acaba-se operando uma grande quantidade de processos
      No fim, separa-se um shared connection pool fora dos processos Python, com algo como pg bouncer, e várias afinações passam a ser necessárias
      No meu caso, no final reimplementei em uma linguagem mais fácil de controlar, com suporte a multithreading e melhor desempenho, e tudo ficou muito mais simples

    • Foi por isso que o CGI acabou evoluindo para modelos que preservam informações entre requisições, como fastcgi

    • Tradicionalmente, também se colocava um daemon independente para atuar como proxy, e usar socket Unix para a conexão é muito mais eficiente do que TCP/IP

    • Alguém sugeriu usar UDP

  • Para mim, inetd é praticamente o próprio CGI
    Isso deixou a internet muito mais divertida
    Foi uma época em que eu fazia rodar vários shell scripts via inetd, até mesmo HTTP escrito só em Bash
    VPS antigos e laptops sem backup nem controle de versão desapareceram, mas ficam as boas lembranças
    O deploy também era simples, com Makefile + scp, e os testes podiam ser escritos em Bash com netcat e grep
    Realmente parecia uma ótima época para viver

  • A impressão é que alcançar 2400 rps em um app hello world não é grande coisa para o hardware atual
    O código nem ficou mais simples, então fica a dúvida: em nome de quê estamos sacrificando desempenho?

    • Se você não precisa processar mais de 2000 rps, então não há problema
      A ideia é que pouquíssimos sites realmente precisam desse volume de tráfego

    • Em números absolutos não parece alto, mas na prática é suficiente para muitos ambientes
      Inclusive seria o bastante para aguentar o "hug of death" que os desenvolvedores do HN costumam mencionar, quando há um pico súbito de acessos

  • Na nossa empresa, ainda usamos diretórios cgi-bin para subir rapidamente webapps internos simples
    Para casos simples, a eficiência de desenvolvimento é muito boa
    Mesmo com CGI, não é preciso dar print manual em HTTP/1.0; dá para usar wsgiref.handlers.CGIHandler do Python e executar qualquer app WSGI como script CGI
    Um exemplo com Flask é simples assim

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

    Em produção, executamos scripts com o plugin CGI do uwsgi
    A sensação é que isso é muito mais simples e flexível do que rodar mod_cgi no Apache ou no lighttpd
    Como o uwsgi roda como unidade de sistema, também dá para aproveitar todo o hardening e sandboxing do systemd
    Além disso, no tratamento CGI do uwsgi é possível definir um interpretador para cada tipo de arquivo

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

    O primeiro byte sai em 250~350 ms, o que é totalmente aceitável para o nosso uso
    documentação do uwsgi sobre CGI

    • Boa dica
      Foi útil saber que wsgiref.handlers.CGIHandler ainda não foi marcado como deprecated
  • Thread relacionada discutida ontem: link

  • Recentemente, ao usar Apache em um projeto paralelo, achei o recurso .htaccess útil
    Basta colocar um arquivo .htaccess em qualquer diretório para carregar configuração adicional do servidor em cada requisição individual
    documentação oficial do htaccess
    Antigamente recomendava-se evitar .htaccess por causa do overhead de acessar disco em toda requisição, e integrar tudo à configuração principal sempre que possível
    Mas hoje, com SSD e RAM sobrando, embora haja uma pequena perda de desempenho, a CPU é boa o bastante para que, na maioria dos casos, isso seja irrelevante
    [meu projeto StaticPatch][https://github.com/StaticPatch/StaticPatch/tree/main] já usa isso na prática

    • Uma frase famosa do criador do PHP, Rasmus Lerdorf
      "Eu não sou realmente um programador de verdade, eu só faço as coisas funcionarem e sigo em frente. Programadores de verdade dizem: 'isso tem muitos memory leaks, precisamos consertar'. Eu simplesmente reinicio o apache a cada 10 requisições"
      O PHP depois percorreu uma longa jornada, superando erros iniciais e evoluindo muito
      Dizem também que ele deixou a frase: "PHP 8 fica melhor quanto menos código meu ele executa"

    • Não entendo por que o Apache não observa o sistema de arquivos e lê só quando há mudança, em vez de causar acessos desnecessários a disco a cada requisição
      A crítica é que, com isso, 99,99% das requisições HTTP acabam ficando mais lentas

  • Tenho pensado nessa estrutura recentemente para prototipagem rápida no meu workflow
    Em linguagens com JIT, se não for em formato fastcgi, o import acaba virando gargalo
    O servidor web h2o, que já usei, tem configuração simples para mruby e handler fast-cgi, então serve perfeitamente para scripts locais
    documentação do fastcgi no h2o
    Outra vantagem é quando se quer permitir que o próprio cliente adicione código customizado para estender um software local
    Por exemplo, antes seria preciso usar MCP para extensões, mas agora basta implementar requisições estruturadas em CGI

    • Para uso em ambiente de usuário final, ligar um programa CGI como front para MCP também parece uma ideia interessante
      Serviços MCP talvez também possam ser implementados tranquilamente como CGI
      Fiquei com vontade de olhar melhor a especificação

    • Fica a dúvida se o fastcgi não elimina quase todas as vantagens que o cgi tinha

  • Antigamente usei diretamente a combinação de programa em C com CGI
    Na época, não havia mais de 100 núcleos nem RAM abundante, e tudo rodava com no máximo 1 GB de memória
    Pensando no que já era possível naquela época, tenho certeza de que hoje seria muito mais fácil

    • Em 1995, a Amazon usava executáveis em C++ via CGI
      Depois que precisou de balanceamento de carga, escalar ficou mais difícil, mas antes disso funcionava bem
      E, por sinal, front-end e backoffice eram dois executáveis separados