Artigo Java Magazine 11 - Testes de carga com JMeter

Projeto antigo do Grupo Apache, o JMeter foi inicialmente concebido para a criação e execução de testes de carga em aplicações web.

Uma das coisas que mais aprecio no vôo livre (faço vôos de parapente) são os preparativos. Gosto de observar todos os apetrechos que levo comigo: GPS, variômetro, bússola, rádio, apito, canivete suíço, lanterna, máquina fotográfica e um monte de coisas mais. O divertido é pensar em como tudo aquilo vai ser útil em determinado momento do vôo. Seria possível voar sem nenhum destes equipamentos, sendo necessário usar apenas a vela e a selete (a cadeira onde o piloto fica alojado), mas o vôo certamente não seria o mesmo. Grande parte dos equipamentos é exigida por segurança; outros são capazes de auxiliar na hora de ganhar altura, ou de saber que lugar exatamente se está sobrevoando.


Guia do artigo:

Mas por que esse papo de vôo livre se a revista é sobre Java? Acontece que tenho a mesma sensação quando ligo meu laptop, abro meu IDE e vejo ali os plug-ins e outros componentes instalados, e que me ajudam na hora de desenvolver, testar, depurar ou otimizar minhas aplicações Java. Eu poderia, é claro, escrever meus sistemas usando apenas o JSDK e um editor de textos, mas o desenvolvimento não seria tão divertido – nem eficaz – do que usando as ferramentas corretas. Uma dessas ferramentas é o Apache JMeter, que apresento neste artigo.

O JMeter

Projeto antigo do Grupo Apache, o JMeter foi inicialmente concebido para a criação e execução de testes de carga em aplicações web. Com ele é possível simular usuários simultâneos acessando diversas páginas e seguindo fluxos de navegação distintos. Como resultado dos testes, o JMeter fornece relatórios e gráficos que auxiliam a visualizar – e compreender – como a aplicação reagiu. São apresentados tempos médios, máximos e mínimos de resposta, o número de requisições aceitas e negadas, e uma série de outras informações. O JMeter vem evoluindo e incorporando mais tipos de testes e relatórios: hoje é possível testar, além de aplicações web, servidores FTP, bancos de dados e web services, entre outros.

Você pode obter uma cópia da ferramenta no site de downloads do Jakarta (veja links). Feito o download, descompacte o pacote em um diretório à sua escolha. Serão criados os seguintes subdiretórios:

Planos de testes

Um plano de testes (Test Plan) representa a navegação de usuários por uma aplicação web. Em uma livraria virtual, por exemplo, essa navegação poderia iniciar-se por uma página de autenticação, indo para a busca de livros, depois para os resultados da busca e seguindo para páginas de resumo e confirmação. Criar um plano de testes nesse caso exigiria representar requisições HTTP e cookies, além de um temporizador para estabelecer pausas entre requisições (já que o cliente passa algum tempo escolhendo livros, por exemplo) – isso para citar apenas alguns elementos.

Nesse sentido, o JMeter oferece componentes que representam grupos de threads, requisições, configurações básicas, sessões e cookies, entre muitos outros necessários para testes de aplicações web. Os componentes são organizados em categorias e podem ser criados e manipulados através do console gráfico da ferramenta.

Primeiro Plano

Vamos criar um plano de testes simples para uma aplicação de exemplo do Tomcat que acessa uma página JSP:

http//localhost:8080/examples/jsp/dates/date.jsp

Inicialize o console gráfico do JMeter chamando jmeter.bat (jmeter, no Linux). Veja a Figura 1. Vamos simular o acesso concorrente de 10 usuários e gerar relatórios com os resultados. O primeiro passo é adicionar um Thread Group, componente responsável pela criação de threads. Clique com o botão direito em Test Plan, na árvore de componentes, e escolha Add > Thread Group (veja a Figura 2); mude o nome para "Primeiro Exemplo" e o número de threads para 10. O campo Ramp-up period é utilizado caso se queria aumentar gradualmente o número de threads durante a execução, o que é útil para verificar a escalabilidade de uma aplicação; não vamos usá-lo nesse primeiro exemplo.

Desmarque o checkbox Forever e altere o valor de Loop Count para 1 (o teste será executado apenas uma vez). A opção Scheduler foi adicionada na versão 1.9.1 e permite agendar um horário para início e o final dos testes, o que é bastante útil para executar testes durante uma determinada quantidade de tempo, sem fixar o número de repetições.

Vamos adicionar um Sampler do tipo HTTP Request, que faz requisições HTTP contra um servidor web. Clique com o botão direito no Thread Group e selecione Add > Samplers> HTTP Request. Defina o Name e altere o endereço do servidor para "localhost" (note que não é incluído o protocolo). Deve-se adicionar a porta caso ela seja diferente da default (80): usaremos a porta padrão do Tomcat: 8080. Escolha também o método HTTP a ser usado, que nesse exemplo simples será GET, e defina o path como /examples/jsp/dates/date.jsp. Veja a Figura 3.

Por último, vamos adicionar dois Listeners para apresentar o resultado dos testes. No menu de contexto do Thread Group, selecione Add > Listener > Aggregate Report. Repita o processo, adicionando o listener View Results in Table.

Você já pode executar os testes, chamando o comando Run > Start. O JMeter criará 10 threads e todas solicitarão a página date.jsp. Os resultados serão apresentados nos dois listeners. O Aggregate Report (Figura 4) apresenta o número total de requisições e os tempos médio, mínimo e máximo de resposta (em milissegundos), além do percentual de erros e da taxa de respostas por segundo. O listener View Results in Table mostra o tempo de resposta de cada solicitação e se ela foi atendida com sucesso (veja a Figura 5).

Terminamos o primeiro exemplo. Você pode salvar o plano de testes (com Save|Test Plan). O arquivo (XML) será gravado com a extensão .jmx.

Defaults, Controladores e Requisições

Vamos a um exemplo mais completo que utiliza sessões, componentes de configuração e outras funcionalidades do JMeter. Usaremos outra aplicação de exemplo do Tomcat, chamada "Calendar", acessível em http://localhost:8080/examples/jsp/cal/login.html. Um fluxo típico para esta aplicação é:

  1. Exibir a página com o formulário de login;
  2. Submeter os dados de login;
  3. Selecionar uma data e um horário para um novo compromisso;
  4. Submeter o compromisso.

O objetivo dos testes será medir o tempo médio de resposta para todas as requisições e identificar a página que mais consome tempo durante a execução. Novamente vamos simular dez usuários simultâneos.

Sabendo o fluxo de navegação e o que pretendemos testar, vamos ao plano de testes. Vamos acessar as seguintes URLs, usando o método HTTP GET para as primeiras três e POST para a última:

Observe que as URLs são muito semelhantes. Imagine como seria mais simples se fosse possível configurar valores default para evitar a entrada do nome do servidor e do número da porta para todas as requisições. Pensando nesses casos, a equipe do JMeter criou os componentes Config Elements, que permitem definir valores padrão para requisições.

Crie um plano de testes (chame-o de "JSP Calendar") e adicione a ele um Config Element do tipo HTTP Request Defaults. Defina as configurações conforme a Figura 6 (observe que foi adicionado o endereço e a porta do servidor).

O suporte a cookies é fundamental nesse exemplo. Adicione outro Config Element, um HTTP Cookie Manager, que é capaz de interceptar uma requisição HTTP, verificar se há cookies em anexo, salvá-los e enviá-los de volta nas requisições posteriores.

O próximo passo é configurar as requisições HTTP. Devemos especificar as URLs e os parâmetros enviados para a aplicação web. Crie um Thread Group e a ele adicione dois elementos do grupo Logic Controller – um Once Only Controller (Figura 7), que irá conter as requisições de Login (que acontecem uma única vez); e um Simple Controller, para a requisição de adição de compromisso.

Adicione um HTTP Request ao Once Only Controller (usando Add > Sampler > HTTP Request) e defina as opções conforme a Figura 8. Observe que os campos Server Name or IP e Port Number ficam em branco – o JMeter buscará essas informações no elemento HTTP Request Defaults criado anteriormente.

Crie mais um HTTP Request (com nome "JSP de Login"), esta requisição utiliza os parâmetros name e email. Para adicioná-los, clique em Add e faça preencha o nome e o valor como na Figura 9.

Adicione outros dois HTTP Requests, agora ao Simple Controller. Faça as configurações como nas Figuras 10 e 11 (lembre-se de preencher o campo Path com os caminhos das URLs mostradas anteriormente). O console do JMeter tem suporte a "Copiar e Colar" e permite arrastar objetos de um grupo para outro. Esta é uma funcionalidade adicionada nas versões mais recentes, e que fez muita falta nos primeiros releases da ferramenta.

Com todos os elementos de requisições adicionados, faltam apenas definir os listeners, para apresentação dos resultados. Crie os seguintes: Aggregate Report, View Results Tree, View Results in Table.

Você já pode executar os testes e verificar os resultados obtidos. No ambiente montado para o artigo, o Aggregate Report informou que a “Página de novo Compromisso” teve a média de tempo de resposta mais alta, 64 ms, mas foi prejudicada por um tempo máximo de 421 ms. O valor de Rate (que significa quantas requisições puderam ser atendidas por segundo) é semelhante para todas as requisições, na casa de 9.7 (Figura 12). Outra coluna a ser observada é count – note que todas as páginas foram executadas 10 vezes, mesmo as que estavam no Once Only Controller, pois o teste foi feito com dez usuários simultâneos.

O View Results Tree mostra informações mais detalhadas sobre as respostas, apresentando o cookie utilizado (em Cookie Data), o tempo de resposta (em Load Time) e a página HTML retornada com duas visualizações: o código fonte e um preview do HTML (veja a Figura 13).

Gravação de Requisições

Se o seu plano de testes fizer a navegação por um site com muitas páginas, a criação de HTTP Requests para cada uma se tornará trabalhosa e repetitiva. Para simplificar, o JMeter oferece um servidor de proxy que intercepta as requisições feitas por um navegador web e cria automaticamente< HTTP Requests. Para o proxy é possível também definir filtros de páginas, evitando criar Requests para imagens ou applets, por exemplo.

Vamos criar um plano de testes utilizando o proxy para simular a navegação de um usuário pelo site da Java Magazine, percorrendo as páginas da matéria “A Escolha Certa” (você pode usar qualquer outro site para simular os testes, é claro). Crie um novo plano de testes e adicione em WorkBech um elemento HTTP Proxy Server, usando Add > Non-Test Elements > HTTP Proxy Server. Defina a porta do proxy – a default é 8080, mas como esta é utilizada pelo Tomcat, usamos a 1010.

Defina também um filtro para que não sejam criados HTTP Requests para imagens e folhas de estilo (.css). Os tipos de arquivos ignorados são definidos usando expressões regulares no campo Patterns to Exclude, no caso: .*\.jpg, .*\.gif e .*\.css.

Adicione um Thread Group e dentro dele um Recording Controller, que é um componente do tipo Logic Controller responsável por gerar os HTTP Requests acessados através do proxy. Inicie o proxy clicando no botão Start (veja a Figura 14).

É necessário configurar seu navegador para fazer uso do proxy. No Internet Explorer 6.0, por exemplo, chame Tools|Internet Options > Connections > LAN Settings e selecione a opção Use a proxy server for your LAN e adicione as entradas específicas (Figura 15). Outros browsers como o Mozilla têm configurações semelhantes.

Com o plano de testes montado, o proxy em execução e o browser configurado, basta navegar pelo site e abrir as páginas desejadas. Ao final será criado no Recording Controller vários HTTP Requests (Figura 16). Se estes não aparecerem, verifique se o browser não está recuperando páginas do cache (nesse caso experimente forçar o recarregamento da página, com CTRL+R ou Shift+F5). Finalizados os testes, você pode parar o proxy.

Se você visitou as quatro páginas do artigo "Ecolha Certa", deve ter agora cinco HTTP Request criados. Em cada um deles, haverá um elemento do tipo HTTP Header Manager, que armazena os atributos de cabeçalhos HTTP enviados pelo servidor, por exemplo Cache-control e User-Agent. As propriedades If-Modified-Since e If-None-Match, adicionadas automaticamente, são responsáveis pelas informações de cache. Se você as mantiver nos HTTP Requests, o servidor HTTP (no caso, o Tomcat) vai assumir que o browser, ou o JMeter, mantém uma cópia da página localmente e que esta será usada, não enviando portanto uma página quando solicitado. Para evitar esse problema, remova do primeiro HTTP Request o elemento Browser-Derived Headers (em HTTP Header Manager).

Adicione também os seguintes listeners ao Thread Group: View Results Tree, Graph Results e Aggregate Report. Altere tanto o número de threads como o Loop Count para 10.

Execute os testes. Você verá que apenas a primeira página realmente foi retornada pelo servidor HTTP, o que fica claro quando analisamos os resultados em View Results Tree. No Aggregate Report, verificamos que a primeira requisição levou em média 4.6 ms para ser retornada (Figura 17).

Observe que o gráfico em Graph Results (Figura 18) apresenta linhas com poucas variações, indicando um tempo de resposta constante. Cada ponto destacado no gráfico representa o tempo de resposta de uma requisição.

Testes em Bancos de Dados

Outros testes que podem ser realizados com o JMeter são de consultas SQL, drivers JDBC e da capacidade de processamento de banco de dados. Como exemplo, vamos acessar um banco de dados HSQLDB (no entanto será fácil portar o exemplo para o banco de dados mais acessível para você). Veja na Listagem 1 o script para criação da tabela com a inserção de alguns registros.

Localize o diretório onde o JMeter foi instalado (por exemplo, c:\jakarta-jmeter-1.9.1) e copie para seu subdiretório lib o driver JDBC do banco. Se o JMeter estiver em execução, reinicie-o para que o driver seja adicionado ao classpath da ferramenta.

Crie um plano de testes e adicione um Config Element do tipo JDBC Database Login Defaults. Este componente armazena as informações de acesso ao banco. Para uma conexão ao HSQLDB use as seguintes configurações:

Por default, o HSQLDB não define uma senha para o usuário “sa”, portanto o campo Password pode ficar vazio.

Adicione também um Config Element do tipo JDBC Database Connection Pool Defaults. Neste componente são definidas configurações para o pool de conexões usado pelo JMeter. Se sua aplicação web faz o acesso ao banco de dados através de um pool (do servidor de aplicações, por exemplo), uma possibilidade é configurá-lo da mesma forma que foi configurado o pool do servidor, ou seja, com o mesmo número máximo de conexões. Dessa forma é possível fazer os testes das consultas SQL num ambiente semelhante ao de execução.

Adicione um Thread Group e, nele, um Simple Controller. Dentro do Controller insira quatro Samplers do tipo JDBC Request que conterão as consultas SQL a serem enviadas ao banco:

Preencha os campos SQL Query String com a consulta SQL; para o campo Name, costumo usar o próprio comando SQL, já que este é mostrado em alguns relatórios (como o Aggregate Report). Os outros campos serão substituídos na execução dos testes, pelos valores definidos nos Config Elements.

Por fim, adicione os listeners Aggregate Report e Graph Results. Execute os testes com um usuário (1 thread), 50 vezes (Loop Count = 50). Os resultados sumarizados são mostrados na Figura 19.

JMeters Simultâneos

Ao utilizar o JMeter para simular vários usuários simultâneos, poderemos ter problemas no lado do cliente quanto ao uso de recursos. Dependendo do número de usuários simulados e da velocidade de resposta do servidor, a máquina cliente poderá ser insuficiente para tratar todas as requisições.

Para resolver este problema é possível rodar instâncias remotas do JMeter em vários computadores da rede, dividindo o trabalho de execução dos testes. As instâncias remotas executam os testes em paralelo e reportam o resultado para um único cliente que faz a sumarização, tornando possível alcançar um grande número de requisições sem sobrecarregar os clientes. Veja mais sobre esse recurso.

Listagem 1. Script para criação da tabela de exemplo.
create table CLIENTES ( ID INTEGER not null, NOME VARCHAR(255) not null, EMAIL VARCHAR(255), CIDADE VARCHAR(255) ); insert into CLIENTES values (1, 'Handerson Ferreira Gomes', 'handerson.gomes@summa-tech.com','Sao Paulo'); insert into CLIENTES values (2,'Joao da Silva', 'joao@silva.com', 'Curitiba'); insert into CLIENTES values (3,'Maria Aparecida', 'maria@aparecida.com', 'Rio de Janeiro');

Elementos do JMeter

Dicas

Planos via Ant

O console gráfico não é a única forma de executar um plano de testes do JMeter. Através de um task do Ant é possível executar um conjunto de planos de testes e gerar relatórios com os resultados obtidos. A integração JMeter/Ant abre a possibilidade de ter uma equipe fazendo testes contínuos de performance, ou testes agendados, sem a necessidade de trabalhar com o console.

Os relatórios são convertidos para HTML usando a task padrão do Ant XSLT. Dois modelos de relatórios são fornecidos: um resumido semelhante ao Aggregate Report e um detalhado. O arquivo de resultados gerado (com extensão .jtl) pode ser aberto no console do JMeter, permitindo com isso acesso a outras visualizações como o Graph Results (para carregar um arquivo .jtl, selecione a opção Browse no menu de contexto de um listener).

Para poder executar um plano do JMeter com o Ant deve-se copiar o arquivo \extras\ant-jmeter\ant-jmeter.jar para o diretório lib do Ant e utilizar a task jmeter no buildfile. Abaixo está um exemplo de buildfile que faz uso da task, além de uma captura do relatório gerado:

target>

Conclusões

Os testes de carga fazem parte de uma etapa fundamental no desenvolvimento de qualquer software multi-usuário. Os resultados obtidos podem identificar problemas de performance ou o uso excessivo de recursos do servidor – e usar uma ferramenta como o JMeter é fundamental nesses casos.

Figura 1. Tela inicial do JMeter
Figura 2. Thread Group configurado.
Figura 3. Primeiro HTTP Request.
Figura 4. Aggregate Report: resultados sumarizados.
Figura 5. View Results in Table: resultados em formato tabular.
Figura 6. Config Element HTTP Request Defaults: valores padrão para requisições HTTP.
Figura 7. Componentes inseridos no Once Only Controller são executados apenas uma vez
Figura 8. HTTP Request da página de login.
Figura 9. HTTP Request de Login, que cria a sessão do usuário.
Figura 10. HTTP Request para a listagem dos compromissos agendados
Figura 11. HTTP Request para o cadastro de um compromisso (observe o uso do método POST e da opção Encode para todos os parâmetros).
Figura 12. Aggregate Report para o exemplo "Calendar".
Figura 13. Resultados hierarquizados com View Results Tree.
Figura 14. Configuração do proxy HTTP do JMeter.
Figura 15. Configuração de proxy no Internet Explorer.
Figura 16. HTTP Request criado pelo Recording Controller.
Figura 17. Aggregate Report para testes de páginas do site www.javamagazine.com.br.
Figura 18. Graph Results com resultado de testes no site da Java Magazine.
Figura 19. Resultados dos testes de bancos de dados.

Confira também

Artigos relacionados