Você acabou de finalizar os últimos retoques no seu site ou aplicação. O cliente já fez exaustivos testes além dos testes realizados por você mesmo. Tudo pronto para colocar em produção aquele código no qual você passou meses trabalhando. E de repente, quando mais de 30 usuários estão fazendo requisições ao mesmo tempo, as requisições demoram a serem respondidas. Parece que a página inicial do site não vai aparecer nunca. Clicar em "salvar" naque formulário de cadastro se torna uma bela desculpa para uma pausa para café.
"Mas como isso está acontecendo?" Você se pergunta. Nos testes realizados as resquisições demoravam poucas dezenas de milisegundos. Como isso foi acontecer? Bom, provavelmente os testes foram realizados com apenas um usuário acessando o servidor, talvez dois. Com o site ou aplicação se tornando popular vários usuários vão fazer seu código ser executado em várias requisições ao mesmo tempo. E é neste momento que estas supresas aparecem.
Para te ajudar a tentar prever como seu código vai se comportar com várias requisições simultâneas existe uma ferramenta muito útil chamada ab (Apache Benchmark), sobre a qual já falei aqui. Em uma máquina com gerenciador de pacotes como o apt-get instalá-la é muito fácil. Basta usar o comando:
#apt-get install apache2-utils
Sua execução básica é assim:
$ab -c 10 -t 30 http://localhost/sitecobaia/
Este comando vai abrir 10 conexões simultâneas e enviar requisições durante 30 segundos para o endereço indicado. Uma variação deste comando é adicionar um parâmetro para que as conexões sejam do tipo keep-alive(comum nas as requisições enviadas por navegadores).
$ab -kc 10 -t 30 http://localhost/sitecobaia/
Quanto aos números você é livre para alterá-los como quiser. Se você for realmente sádico coloque algo como 100 requisições simultâneas durante 60 segundos e veja seu servidor sofrer.
Um exemplo de saída ao comando ab pode ser visto abaixo:
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Finished 484 requests
Server Software: Apache/2.2.20
Server Hostname: localhost
Server Port: 80
Document Path: /sitecobaia/
Document Length: 43666 bytes
Concurrency Level: 10
Time taken for tests: 30.022 seconds
Complete requests: 484
Failed requests: 0
Write errors: 0
Keep-Alive requests: 0
Total transferred: 21502202 bytes
HTML transferred: 21134344 bytes
Requests per second: 16.12 [#/sec] (mean)
Time per request: 620.291 [ms] (mean)
Time per request: 62.029 [ms] (mean, across all concurrent requests)
Transfer rate: 699.43 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 2
Processing: 349 614 108.0 608 950
Waiting: 344 598 105.6 591 932
Total: 349 614 108.0 608 950
Percentage of the requests served within a certain time (ms)
50% 608
66% 658
75% 688
80% 707
90% 750
95% 798
98% 845
99% 875
100% 950 (longest request)
Alguns números mostrados são interessantes. Como por exemplo a média de requisições por segundo(Requests per second) que neste caso foi de 16,12. Quanto mais alto este número mais requisições seu servidor servirá em menos tempo. A média de tempo(Time per request) por requisição também é interessante de ser observada. Neste caso temos dois valores, o primeiro, indica o tempo médio em milisegundos que o servidor demorou para responder uma única requisição, neste caso 620,291 ms. Ou seja com 10 conexões concorrentes a resposta demourou em média pouco mais de meio segundo para chegar(1 segundo tem 1000 milisegundos). O segundo valor da média de tempo por requisição é o resultado da divisão do tempo total para todas as requisições(30,22 segundos ou 30022 milisegundos) pelo número de requisições completas (484) o que dá o valor de 62,029 milisegundos por requisição.
No final temos um resumo da porcentagem de requsições respondidas dentro de um certo tempo em milisegundos. Observem que 50% das requisições demoraram no máximo 608 ms para serem respondidas. E a requisição mais demorada demorou 950 ms.
Um resultado que pode deixar-nos confusos quando usamos o ab com sites dinâmicos é o seguinte:
Failed requests: 32951
(Connect: 0, Receive: 0, Length: 32951, Exceptions: 0)
Você pensa: "Porque ele reportou que 32951 requisições falharam?". Mas observe que o tipo de erro foi de tamanho(Length), isso acontece porque o ab considera que quando os tamanhos das respostas diferem da primeira resposta obtida do servidor acontece um erro de Length. Como em páginas dinâmicas coisas como identificadores de sessão, cookies ou até mesmo o conteúdo da resposta podem mudar de uma requisição para outra este tipo de "erro" pode aparecer frequentemente.
Mostrando postagens com marcador desempenho. Mostrar todas as postagens
Mostrando postagens com marcador desempenho. Mostrar todas as postagens
terça-feira, 19 de fevereiro de 2013
domingo, 5 de julho de 2009
Clonando objetos em PHP
Estes dias eu estava escrevendo um código que parecia mais ou menos assim (está simplificado aqui):
Eu achava que não tinha necessidade de ficar instanciando a variável $robo dentro do loop para poder ter diferentes instancias dela (e isso teria funcionado no php versão 4, alguém ainda usa esta versão?). No php 5 agora todos os objetos são passados por referência (coisa feia eu não saber disso, eu sei). Mas esta versão do php providencia uma palavra chave chamada clone. Exemplo abaixo:
Compare os dois códigos rodando para você ver a diferença. Os objetos dentro do array no código usando clone ficam com a sequência correta. Uma forma de arrumar o primeiro código sem usar o clone seria instanciando a variável $robo dentro do loop ao invés dela receber a variável $instancia_robo. Os próximos códigos tentam fazer um comparativo do tempo que cada comando leva (um usando clone e outro instanciando a variável dentro do loop).
Clone:
Instância:
Rodando no meu computador (Dual core 1.73 GHZ, 2 GB RAM) as diferenças de tempo de execução dos dois códigos foram pequenas.
Comparando:
Clone
1 - 3,01864814758
2 - 3,74507808685
3 - 2,97225308418
Média: 3,24532644
Instância
1 - 3,50589299202
2 - 4,54684591293
3 - 3,42544102669
Média: 3,826059977
É claro que o teste não tem muito rigor. Provavelmente os resultados dependem mais da complexidade da classe do objeto. Se tivéssemos usado uma classe com métodos mágicos como __construct provavelmente as diferenças seriam maiores a favor do comando clone.
$instancia_robo = new Robo();
$robos = array();
for($i = 0; $i < 5; $i++) {
$robo = $instancia_robo;
$robo->id = $i;
$robos[] = $robo;
}
var_dump($robos);
Eu achava que não tinha necessidade de ficar instanciando a variável $robo dentro do loop para poder ter diferentes instancias dela (e isso teria funcionado no php versão 4, alguém ainda usa esta versão?). No php 5 agora todos os objetos são passados por referência (coisa feia eu não saber disso, eu sei). Mas esta versão do php providencia uma palavra chave chamada clone. Exemplo abaixo:
echo 'Usando clone';
$instancia_robo = new Robo();
$robos = array();
for($i = 0; $i < 5; $i++) {
$robo = clone $instancia_robo;
$robo->id = $i;
$robos[] = $robo;
}
var_dump($robos);
Compare os dois códigos rodando para você ver a diferença. Os objetos dentro do array no código usando clone ficam com a sequência correta. Uma forma de arrumar o primeiro código sem usar o clone seria instanciando a variável $robo dentro do loop ao invés dela receber a variável $instancia_robo. Os próximos códigos tentam fazer um comparativo do tempo que cada comando leva (um usando clone e outro instanciando a variável dentro do loop).
Clone:
ini_set("memory_limit","512M");
class Robo {
public $id;
}
define('MAX_NUMEROS', 1000000);
echo 'Usando clone', '
';
$instancia_robo = new Robo();
$robos = array();
$start= microtime(TRUE);
for($i = 0; $i < MAX_NUMEROS; $i++) {
$robo = clone $instancia_robo;
$robo->id = $i;
$robos[] = $robo;
}
$finish = microtime(TRUE);
echo 'Tempo decorrido ', $finish - $start;
Instância:
ini_set("memory_limit","512M");
class Robo {
public $id;
}
define('MAX_NUMEROS', 1000000);
echo 'Instanciando', '
';
$robos = array();
$start = microtime(TRUE);
for($i = 0; $i < MAX_NUMEROS; $i++) {
$robo = new Robo;
$robo->id = $i;
$robos[] = $robo;
}
$finish = microtime(TRUE);
echo 'Tempo decorrido ', $finish - $start, '
';
Rodando no meu computador (Dual core 1.73 GHZ, 2 GB RAM) as diferenças de tempo de execução dos dois códigos foram pequenas.
Comparando:
Clone
1 - 3,01864814758
2 - 3,74507808685
3 - 2,97225308418
Média: 3,24532644
Instância
1 - 3,50589299202
2 - 4,54684591293
3 - 3,42544102669
Média: 3,826059977
É claro que o teste não tem muito rigor. Provavelmente os resultados dependem mais da complexidade da classe do objeto. Se tivéssemos usado uma classe com métodos mágicos como __construct provavelmente as diferenças seriam maiores a favor do comando clone.
Assinar:
Postagens (Atom)