Depois de apanhar muito de servidores web lotados com centenas de requisições simultâneas, resolvi dar uma pesquisada na net para entender os motivos da baixa performance e solucionar definitivamente o problema.
O intuito não era criar um cluster com vários servidores respondendo pelo mesmo nome, mas aproveitar ao máximo os recursos de um servidor. Vou relatar aqui as configurações que considero mais importantes, ou seja, as que produziram melhorias perceptíveis. Não vou entrar na discussão de qual o interpretador é mais eficiente (php , python, perl ou afins). Use ao seu gosto.
Um fator de extrema importância para um servidor web é a frequência com que ele utiliza swap. Devido a natureza do serviço, espera-se sempre uma resposta rápida do servidor e o processo de swap é um fator altamente degradante para o tempo de resposta. Quando a memória física (RAM) se esgota, o sistema operacional salva algumas informações pouco acessadas da memória no disco rígido em um processo denominado troca ou swap. Esse processo, apesar de necessário, não deve ser utilizado com frequência, pois a leitura e a escrita no disco são de 10 a 100 vezes mais lentas do que na RAM, dependendo da máquina. Por isso devemos dimensionar o servidor web baseado exclusivamente na quantidade de memória RAM.
Façamos a seguinte conta:
Quantidade de memória RAM total - (menos) Quantidade de memória ocupada por programas quando o servidor web está desligado.
Agora vamos dividir o resultado pela quantidade média de memória ocupada por um processo filho do Apache (em torno 3M quando utiliza-se html puro, sem nenhuma linguagem interpretada e em torno de 20M para html+php) - você pode utilizar o "top" para descobrir esse valor.
O resultado obtido da divisão anterior deve ser o número máximo de clientes que seu host pode atender simultaneamente sem utilizar swap. Não se espante se o número for baixo (20, 30, 50,..). Mais adiante vamos fazer algumas contas que vão mostrar quantas requisições o seu servidor pode atender por minuto. Coloque este número como o valor MaxClients do Apache.
Se o seu host trabalha sempre perto da capacidade máxima, configure o valor do StartServers, MinSpareServers e MaxSpareServers próximo ao valor do MaxClients (ex:80%), se não você pode trabalhar com um valor perto de 50% ou menos. Perceba que não há razão para utilizar valores de StartServers e MinSpareServers diferentes, já que o serviço vai iniciar com o número de processos igual a StartServers e vai aumentar até atingir o número de MinSpareServers em poucos segundos. Diferenciar MinSpareServers e MaxSpareServers só fará sentido se seu servidor alterna entre períodos de grande e pequena demanda.
Vejamos agora um pouco do conceito de KeepAlive. Este recurso do Apache permite que um cliente faça várias requisições utilizando uma mesma conexão TCP. Isso economiza tempo e processamento, evitando que novos sockets e novos processos sejam criados. A conexão TCP é terminada quando o cliente passa um tempo sem fazer requisições (KeepAliveTimeout) ou quando o processo responsável pela conexão atender o número de requisições igual a MaxKeepAliveRequests.
Portanto utilizar este recurso é bom, mas devemos tomar cuidado para não ocupar um processo por muito tempo, evitando que novos clientes sejam atendidos. Valores que tem se mostrado razoáveis para o KeepAliveTimeout estão entre 1 e 5 segundos (é claro que utilizar um ou outro valor é uma decisão que você deve tomar baseado na quantidade de usuários e na qualidade do serviço que você quer prestar - valores mais baixos proporcionam maior rotatividade e valores mais altos maior qualidade no serviço do usuário que está sendo atendido. É como um médico cuja consulta dura 10 minutos e outro que dura 50).
O mesmo conceito se aplica para o MaxKeepAliveRequests. Quanto maior esse número, mais requisições um mesmo usuário poderá fazer antes de liberar a conexão para outro. Valores típicos estão entre 100 e 500 (0 significa que não há limite).
Agora vamos fazer mais algumas contas. Supondo que, pelos cálculos anteriores, você possa atender 20 clientes simultaneamente e que você escolha 1 segundo para o KeepAliveTimeout. Na melhor das hipóteses e em uma situação ideal o servidor seria capaz de atender 1200 novas conexões por minuto. É claro que esse valor não representa 1200 usuários simultâneos, porque um mesmo usuário pode abrir novas conexões a medida que vai navegando pelo site. Perceba que limitar o número de conexões simultâneas a um número pequeno não significa necessariamente que você só poderá atender poucos usuários.
A título de exemplo, vamos considerar um servidor com 512M de RAM que serve páginas web dinâmicas feitas em PHP e que os processos residentes, desconsiderando o Apache, ocupam 100M da memória RAM. Então,
512 - 100 = 412
412 / 20 = 20 e uns quebrados
Uma configuração que provavelmente otimizaria o seu Apache seria:
KeepAlive On
KeepAliveTimeout 1
MaxKeepAliveRequests 100
StartServers 15
MinSpareServers 15
MaxSpareServers 15
MaxClients 20
MaxRequestsPerChild 0
O valor de MaxRequestsPerChild representa o número de requisições que um processo filho do Apache pode atender antes de ser forçado a terminar. Isso era muito utilizado em sistemas operacionais com problemas de vazamento de memória (ex:Solaris). Se você utiliza
Linux ou BSD é seguro utilizar 0 (infinito). Caso você note que depois de alguns dias a quantidade de memória utilizada por um processo está crescendo de forma anormal, mude esse valor para um valor finito porém grande (entre 5000 e 50000).
Além disso, se as aplicações que você hospeda demandam PHP, considere utilizar uma extensão que faça cache do código intermediário (APC , Turck MMCache). Essas extensões podem tornar o acesso até 5 vezes mais rápido.
Referências: