Dimensionar um servidor para uma aplicação Python tem uma particularidade que confunde quem vem do mundo dos sites: o número de acessos simultâneos que a aplicação suporta é definido por uma configuração, e não pela capacidade do servidor.
Em um site em PHP, o servidor web administra os processos conforme a demanda. Em uma aplicação Python, você define quantos trabalhadores vão existir — e esse número é o teto rígido de requisições que podem ser atendidas ao mesmo tempo.
Isso significa que uma aplicação pode estar recusando requisições com o servidor pela metade da capacidade, simplesmente porque foi configurada com poucos trabalhadores. E pode derrubar o servidor com muitos, por consumo de memória.
Este guia mostra como chegar a esse número com base em medição. Ele pressupõe conhecida a arquitetura de produção descrita no artigo sobre hospedar aplicações Python em produção.
O que determina a capacidade
Três números, e a relação entre eles:
| Fator | O que define |
|---|---|
| Trabalhadores | Quantas requisições são atendidas simultaneamente |
| Memória por trabalhador | Quantos cabem no servidor |
| Tempo por requisição | Quantas requisições cada trabalhador atende por minuto |
A conta que decorre: trabalhadores multiplicados pela memória de cada um precisa caber na memória do servidor, com folga para o sistema e para o banco de dados, se ele estiver no mesmo lugar.
E a capacidade real de atendimento é o número de trabalhadores dividido pelo tempo médio de cada requisição. Uma aplicação com quatro trabalhadores e requisições de meio segundo atende muito mais que uma com oito trabalhadores e requisições de cinco segundos.
Por isso reduzir o tempo das requisições costuma render mais que aumentar trabalhadores — e é bem mais barato.
O que medir
Seis números, obtidos com a aplicação sob carga real ou simulada:
- Memória por trabalhador em regime, depois de horas atendendo requisições — não logo após iniciar.
- Tempo médio de resposta por rota, com atenção às mais lentas.
- Requisições simultâneas no pico, não o total diário.
- Proporção de tempo esperando — banco de dados, APIs externas, disco — versus processando.
- Conexões abertas com o banco, somadas entre todos os trabalhadores.
- Memória total do processo ao longo do tempo, para detectar crescimento contínuo.
O item 4 é o que orienta a configuração, e merece explicação.
Uma aplicação que passa a maior parte do tempo esperando — consultando banco, chamando serviços externos — deixa o processador ocioso durante essa espera. Existem modelos de trabalhador que aproveitam isso, atendendo mais requisições simultâneas por processo.
Uma aplicação que passa a maior parte do tempo processando não se beneficia disso: o trabalho é real e ocupa o processador de fato.
A medição do item 6 é o alarme mais importante: se a memória cresce continuamente sem estabilizar, existe um vazamento — e dimensionar apenas adia o problema. Uma aplicação saudável estabiliza em um patamar e oscila em torno dele.
Quantos trabalhadores
A pergunta prática.
Existem fórmulas de ponto de partida baseadas no número de núcleos do servidor, e elas servem como ponto de partida — não como resposta. O que as torna insuficientes é que elas ignoram a memória, que costuma ser o limite real.
Um método que funciona melhor:
- Meça a memória de um trabalhador em regime.
- Calcule quantos cabem na memória do servidor, reservando espaço para o sistema, o servidor web e o banco de dados, se local.
- Compare com o ponto de partida baseado em núcleos. Use o menor dos dois números.
- Ajuste conforme o perfil. Aplicações que esperam muito comportam configurações que atendem mais simultâneos por processo.
- Teste com carga e observe se as requisições entram em fila.
- Deixe margem. Configurar no limite exato significa que qualquer variação derruba o servidor.
O passo 3 é o que evita o erro mais comum: seguir uma fórmula de núcleos em um servidor com pouca memória. Quando ela esgota, o sistema operacional encerra processos, e o resultado são falhas intermitentes que parecem aleatórias.
Um detalhe que economiza memória: alguns modos de inicialização permitem que os trabalhadores compartilhem parte da memória carregada antes da bifurcação dos processos. O ganho é real em aplicações que carregam bibliotecas grandes, e vale verificar na documentação da ferramenta em uso.
Conexões de banco: o limite escondido
Um teto que trava aplicações aparentemente bem dimensionadas.
Cada trabalhador mantém as próprias conexões com o banco de dados. O total é o número de trabalhadores multiplicado pelas conexões que cada um abre — e esse total precisa caber no limite configurado no banco.
O sintoma é característico: a aplicação funciona bem até certo volume e então passa a falhar com erros de conexão, mesmo com processador e memória confortáveis.
Duas soluções que se complementam:
- Agrupador de conexões, que mantém um conjunto controlado e reutilizado em vez de abrir uma nova a cada necessidade.
- Conexões persistentes com limite, quando a ferramenta permite, reduzindo o custo de abrir e fechar a cada requisição.
Em aplicações com muitos trabalhadores, isso deixa de ser otimização e vira requisito.
Trabalhadores de fila contam também
Um erro de dimensionamento frequente e fácil de evitar.
Se a aplicação usa uma fila de tarefas — e a maior parte das aplicações Python de porte usa —, os processos que executam essas tarefas são processos adicionais, com consumo próprio de memória e de conexões de banco.
Isso significa que a conta precisa incluir:
- Trabalhadores da aplicação, que atendem requisições.
- Trabalhadores da fila, que executam tarefas em segundo plano.
- O agendador de tarefas, se houver.
- As conexões de banco de todos eles, somadas.
E há uma diferença importante: trabalhadores de fila frequentemente consomem mais memória que os da aplicação, porque executam operações pesadas — processar arquivos, gerar relatórios, manipular imagens. Dimensioná-los como se fossem iguais subestima o consumo.
O conceito e os requisitos de fila estão no artigo sobre o que é uma fila de tarefas.
O que fazer antes de aumentar recursos
Como sempre, as hipóteses baratas primeiro:
- Reduza o tempo das requisições lentas. Cada segundo economizado multiplica a capacidade de cada trabalhador.
- Verifique as consultas ao banco. Consultas sem índice adequado ou que buscam dados em excesso são a causa mais comum de requisições lentas.
- Mova trabalho pesado para a fila, liberando trabalhadores para atender usuários.
- Sirva arquivos estáticos pelo servidor web, e não pela aplicação.
- Adicione cache onde fizer sentido — resultados de consultas repetidas, respostas de APIs externas.
- Investigue o consumo de memória antes de assumir que a aplicação precisa de mais.
- Confirme que não há código bloqueante em rotas que deveriam ser rápidas.
O item 2 rende mais que todos os outros na maioria dos casos. Uma consulta lenta não melhora com mais trabalhadores — ela apenas passa a ser executada em paralelo, aumentando a carga do banco e podendo piorar a situação.
Quando a restrição é real
Se depois dos ajustes a aplicação continua saturando, a ordem de prioridade:
- Memória, que é o limite mais frequente e o que define quantos trabalhadores cabem.
- Processamento, relevante em aplicações que fazem trabalho intensivo.
- Banco em servidor próprio, quando as consultas são o gargalo.
- Distribuição entre servidores, quando um único já não comporta.
Sobre o último item: distribuir exige que a aplicação não guarde estado local entre requisições, que arquivos enviados fiquem em armazenamento compartilhado e que as sessões sejam centralizadas. São decisões de projeto, e a ressalva é a mesma feita no comparativo entre VPS e Cloud Server — redundância não vem junto com o tipo de hospedagem.
Sinais de que está mal dimensionado
| Sintoma | Causa provável |
|---|---|
| Requisições em fila com recursos livres | Trabalhadores insuficientes |
| Falhas intermitentes sem padrão | Memória esgotando e processos encerrados |
| Erro de conexão em volume médio | Limite de conexões do banco atingido |
| Lentidão progressiva ao longo dos dias | Vazamento de memória |
| Uma rota lenta e o resto normal | Consulta ou operação específica |
| Tarefas de fila atrasando | Trabalhadores de fila insuficientes |
A segunda linha é a mais difícil de diagnosticar e a mais comum em aplicações recém-publicadas: configurar trabalhadores demais para a memória disponível produz falhas que parecem aleatórias, porque dependem de quais processos estavam ativos no momento.
Conclusão
Dimensionar uma aplicação Python é definir quantos trabalhadores existem — e esse número é limitado pela memória de cada um, não por uma fórmula de núcleos. Configurar acima do que a memória comporta produz falhas intermitentes; configurar abaixo recusa requisições com o servidor ocioso.
Duas medições resolvem a maior parte: memória por trabalhador em regime e tempo médio de resposta. E dois limites escondidos derrubam aplicações bem dimensionadas: as conexões de banco somadas entre todos os processos, e os trabalhadores de fila, que consomem mais do que se imagina e costumam ficar fora da conta.
Antes de contratar mais recursos, a verificação que rende mais: reduzir o tempo das requisições lentas, que quase sempre são consultas ao banco. Conheça o Cloud Server para Python da TBF Host e avalie a configuração a partir dos números que você mediu.
Perguntas frequentes
Quantos trabalhadores devo configurar em uma aplicação Python?
O número que couber na memória do servidor, comparado com um ponto de partida baseado nos núcleos disponíveis — use o menor dos dois. Meça a memória de um trabalhador em regime, reserve espaço para o sistema, o servidor web e o banco se for local, e deixe margem para variações.
Por que minha aplicação Python recusa requisições com o servidor ocioso?
Porque o número de trabalhadores é um teto rígido de requisições simultâneas, definido por configuração e não pela capacidade do servidor. Com poucos trabalhadores, as requisições entram em fila mesmo havendo memória e processador disponíveis.
Quanta memória uma aplicação Python precisa?
Depende do consumo de cada trabalhador multiplicado pela quantidade deles, mais o sistema, o servidor web e o banco se estiver no mesmo servidor. O valor a medir é o consumo em regime, depois de horas atendendo requisições — e não logo após iniciar, que é sempre menor.
Por que minha aplicação tem falhas intermitentes sem padrão?
Uma causa comum é configurar mais trabalhadores do que a memória comporta. Quando ela esgota, o sistema operacional encerra processos, e o resultado são falhas que parecem aleatórias porque dependem de quais processos estavam ativos naquele momento.
Por que a aplicação falha com erro de conexão em volume médio?
Provavelmente o limite de conexões do banco de dados foi atingido. Cada trabalhador mantém as próprias conexões, e o total é o número de trabalhadores multiplicado pelas conexões de cada um. Um agrupador de conexões resolve, e em aplicações com muitos trabalhadores ele é requisito.
Trabalhadores de fila entram no dimensionamento?
Sim, e são frequentemente esquecidos. Eles são processos adicionais, com consumo próprio de memória e de conexões de banco — e costumam consumir mais que os trabalhadores da aplicação, porque executam operações pesadas como processar arquivos e gerar relatórios.
O que fazer antes de aumentar os recursos do servidor?
Reduzir o tempo das requisições lentas, que quase sempre são consultas ao banco sem índice adequado ou que buscam dados em excesso. Cada segundo economizado multiplica a capacidade de cada trabalhador — e uma consulta lenta não melhora com mais trabalhadores, apenas passa a rodar em paralelo.
Minha aplicação fica progressivamente mais lenta ao longo dos dias. O que é?
Provavelmente um vazamento de memória: o consumo cresce continuamente sem estabilizar. Nesse caso, dimensionar apenas adia o problema, aumentando o intervalo entre reinícios. Uma aplicação saudável estabiliza em um patamar de memória e oscila em torno dele.