Estou executando um site de tráfego relativamente baixo que apresenta um grande aumento de visitantes uma vez por semana após uma atualização do site. Durante esse pico, o desempenho do site é extremamente ruim comparado ao resto da semana. A carga real nos servidores permanece muito baixa, com confiabilidade inferior a 10% da CPU e menos de 30% de RAM (o hardware deve ser um exagero total para o que realmente estamos fazendo), mas, por alguma razão, o Apache parece não conseguir lidar com a quantidade de pedidos. Estamos executando o apache 2.2.3 no RHEL 5.7, kernel 2.6.18-274.7.1.el5, x86_64.
Tentando reproduzir esse comportamento durante horas fora com ab, estou encontrando uma grande queda no desempenho ao exceder aproximadamente 256 usuários. A execução do teste com o menor caso de uso possível que eu pude apresentar (arquivo de texto estático sendo recuperado, total de 223 bytes), o desempenho é sempre normal com 245 solicitações simultâneas:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 15 25 5.8 24 37
Processing: 15 65 22.9 76 96
Waiting: 15 64 23.0 76 96
Total: 30 90 27.4 100 125
Percentage of the requests served within a certain time (ms)
50% 100
66% 108
75% 111
80% 113
90% 118
95% 120
98% 122
99% 123
100% 125 (longest request)
Mas assim que eu rastreio até 265 solicitações simultâneas, um subconjunto delas começa a levar uma quantidade absurda de tempo para concluir:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 13 195 692.6 26 3028
Processing: 15 65 21.3 72 100
Waiting: 15 65 21.3 71 99
Total: 32 260 681.7 101 3058
Percentage of the requests served within a certain time (ms)
50% 101
66% 108
75% 112
80% 116
90% 121
95% 3028
98% 3040
99% 3044
100% 3058 (longest request)
Esses resultados são muito consistentes em várias execuções. Como há outro tráfego indo para essa caixa, não tenho certeza exatamente onde estaria o corte final, se houver, mas parece suspeitamente próximo de 256.
Naturalmente, presumi que isso estava sendo causado pelo limite de encadeamentos no prefork, então fui em frente e ajustei a configuração para dobrar o número de encadeamentos disponíveis e para impedir que o conjunto de encadeamentos crescesse e encolhesse desnecessariamente:
<IfModule prefork.c>
StartServers 512
MinSpareServers 512
MaxSpareServers 512
ServerLimit 512
MaxClients 512
MaxRequestsPerChild 5000
</IfModule>
mod_status confirma que agora estou executando com 512 threads disponíveis
8 requests currently being processed, 504 idle workers
No entanto, tentar 265 solicitações simultâneas ainda gera resultados quase idênticos aos de antes
Connection Times (ms)
min mean[+/-sd] median max
Connect: 25 211 714.7 31 3034
Processing: 17 94 28.6 103 138
Waiting: 17 93 28.5 103 138
Total: 57 306 700.8 138 3071
Percentage of the requests served within a certain time (ms)
50% 138
66% 145
75% 150
80% 161
90% 167
95% 3066
98% 3068
99% 3068
100% 3071 (longest request)
Depois de vasculhar a documentação (e o Stack Exchange), não consigo mais configurações para tentar resolver esse gargalo. Há algo que estou perdendo? Devo começar a procurar respostas fora do apache? Alguém já viu esse comportamento? Qualquer ajuda seria muito apreciada.
EDITAR:
Seguindo o conselho de Ladadadada, corri contra o apache. Tentei com -tt e -T algumas vezes e não consegui encontrar nada fora do comum. Tentei executar strace -c em todos os processos apache atualmente em execução e obtive o seguinte:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
22.09 0.317836 5 62128 4833 open
19.91 0.286388 4 65374 1896 lstat
13.06 0.187854 0 407433 pread
10.70 0.153862 6 27076 semop
7.88 0.113343 3 38598 poll
6.86 0.098694 1 100954 14380 read
(... encurralado)
Se eu estiver lendo isso direito (e concordar comigo, como não uso o strace com muita frequência), nenhuma das chamadas do sistema pode explicar a quantidade de tempo que essas solicitações estão demorando. Parece que o gargalo está ocorrendo antes mesmo das solicitações chegarem aos segmentos de trabalho.
EDIT 2:
Como várias pessoas sugeriram, eu executei o teste novamente no próprio servidor da Web (anteriormente o teste era executado em um local neutro da Internet). Os resultados foram surpreendentes:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 11 6.6 12 21
Processing: 5 247 971.0 10 4204
Waiting: 3 245 971.3 7 4204
Total: 16 259 973.3 21 4225
Percentage of the requests served within a certain time (ms)
50% 21
66% 23
75% 24
80% 24
90% 26
95% 4225
98% 4225
99% 4225
100% 4225 (longest request)
O tempo final é semelhante ao teste baseado na Internet, mas parece ser um pouco pior quando executado localmente. Mais interessante, o perfil mudou dramaticamente. Considerando que antes que a maior parte do tempo das solicitações de execução longa fosse gasta em "conexão", agora o gargalo parece estar em processamento ou em espera. Suspeito que esse possa realmente ser um problema separado que anteriormente estava sendo mascarado por limitações de rede.
Executando o teste novamente de outra máquina na mesma rede local que o host Apache, estou vendo resultados muito mais razoáveis:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 2 0.8 2 4
Processing: 13 118 99.8 205 222
Waiting: 13 118 99.7 204 222
Total: 15 121 99.7 207 225
Percentage of the requests served within a certain time (ms)
50% 207
66% 219
75% 220
80% 221
90% 222
95% 224
98% 224
99% 225
100% 225 (longest request)
Esses dois testes juntos levantam uma série de perguntas, mas separadamente disso, agora há um argumento convincente a ser feito para algum tipo de gargalo grave na rede que ocorre sob uma certa carga. Eu acho que os próximos passos serão investigar a camada de rede separadamente.