Teste de carga com gafanhoto

O teste de carga não é tão procurado e difundido quanto outros tipos de teste - não existem tantas ferramentas que permitem a realização de tais testes, mas geralmente simples e conveniente podem ser contados com os dedos de uma mão.

Quando se trata de teste de desempenho - antes de tudo, todos pensam no JMeter - é sem dúvida a ferramenta mais famosa com o maior número de plugins. Eu nunca gostei do JMeter por causa de sua interface não óbvia e alto limite de entrada, assim que for necessário testar um aplicativo que não seja o Hello World.

E agora, inspirado pelo sucesso dos testes em dois projetos diferentes, decidi compartilhar informações sobre um software relativamente simples e conveniente - Locust

Para quem tem preguiça de gravar, gravei um vídeo:



O que é isso


Ferramenta de código aberto que permite especificar cenários de carregamento com código Python que suporta carregamento distribuído e, como afirmam os autores, foi usada para testes de estresse do Battlelog para a série de jogos Battlefild (imediatamente cativante)

Dos profissionais:

  • documentação simples, incluindo exemplo de copiar e colar. Você pode começar a testar, mesmo com quase nenhuma habilidade de programação.
  • "Under the hood" usa a biblioteca de solicitações (HTTP para pessoas). Sua documentação pode ser usada como uma folha de dicas e testes de débito estendidos
  • Suporte para Python - eu gosto da linguagem
  • O parágrafo anterior fornece uma plataforma cruzada para a execução de testes
  • Servidor Web Flask próprio para exibição dos resultados dos testes

Dos menos:

  • Sem captura e repetição - todas as mãos
  • O resultado do parágrafo anterior - você precisa de um cérebro. Como no Postman, você precisa entender como o HTTP funciona.
  • Necessidades mínimas de programação necessárias
  • Modelo de carga linear - que incomoda imediatamente os fãs para gerar usuários "de acordo com Gauss"

Processo de teste


Qualquer teste é uma tarefa complexa que requer planejamento, preparação, monitoramento da implementação e análise de resultados. Durante o teste de estresse, se possível, é possível e necessário coletar todos os dados possíveis que podem afetar o resultado:

  • Hardware do servidor (CPU, RAM, ROM)
  • Software de servidor (SO, versão do servidor, JAVA, .NET etc.), o banco de dados e a quantidade de dados em si, os logs do servidor e do aplicativo de teste
  • Largura de banda da rede
  • A presença de servidores proxy, balanceadores de carga e proteção DDOS
  • Carregar dados de teste (número de usuários, tempo médio de resposta, número de solicitações por segundo)

Os exemplos descritos abaixo podem ser classificados como teste de carga funcional de caixa preta. Mesmo sem saber nada sobre o aplicativo em teste e sem acesso aos logs, podemos medir seu desempenho.

Antes de começar


Para testar os testes de carga na prática, implantei um servidor Web localmente simples https://github.com/typicode/json-server . Vou dar quase todos os exemplos a seguir para ele. Tirei os dados do servidor de um exemplo online implantado - https://jsonplaceholder.typicode.com/
Para executá-lo, o nodeJS é necessário.

Spoiler óbvio : como nos testes de segurança - é melhor realizar experimentos com testes de estresse em gatos localmente, sem carregar serviços on-line, para que você não seja banido

Para começar, o Python também é necessário - em todos os exemplos, usarei a versão 3.6, bem como as próprias gafanhotos (no momento da redação, versão 0.9.0). Pode ser instalado com o comando

python -m pip install locustio 

Os detalhes da instalação podem ser encontrados na documentação oficial.

Analisando um Exemplo


Em seguida, precisamos de um arquivo de teste. Peguei um exemplo da documentação, pois é muito simples e direto:

 from locust import HttpLocust, TaskSet def login(l): l.client.post("/login", {"username":"ellen_key", "password":"education"}) def logout(l): l.client.post("/logout", {"username":"ellen_key", "password":"education"}) def index(l): l.client.get("/") def profile(l): l.client.get("/profile") class UserBehavior(TaskSet): tasks = {index: 2, profile: 1} def on_start(self): login(self) def on_stop(self): logout(self) class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 5000 max_wait = 9000 

Isso é tudo! Isso é realmente o suficiente para iniciar o teste! Vejamos um exemplo antes de começarmos.

Ignorando as importações, no início, vemos duas funções quase idênticas de login e logout, consistindo em uma linha. l.client - um objeto de sessão HTTP com o qual criaremos uma carga. Usamos o método POST, que é quase idêntico ao da biblioteca de solicitações. Quase - porque neste exemplo, estamos passando como o primeiro argumento, não um URL completo, mas apenas parte dele - um serviço específico.

O segundo argumento passa os dados - e não posso deixar de notar que é muito conveniente usar dicionários Python, que são automaticamente convertidos em json

Você também pode observar que não processamos o resultado da solicitação de forma alguma - se for bem-sucedida, os resultados (por exemplo, cookies) serão salvos nesta sessão. Se ocorrer um erro, ele será gravado e adicionado às estatísticas sobre a carga.

Se quisermos saber se escrevemos a solicitação corretamente, sempre podemos verificar da seguinte forma:

 import requests as r response=r.post(base_url+"/login",{"username":"ellen_key","password":"education"}) print(response.status_code) 

Eu adicionei apenas a variável base_url , que deve conter o endereço completo do recurso que está sendo testado.

As próximas funções são consultas, que criarão uma carga. Novamente, não precisamos processar a resposta do servidor - os resultados irão imediatamente para as estatísticas.

A seguir é a classe UserBehavior (o nome da classe pode ser qualquer um). Como o nome indica, ele descreverá o comportamento de um usuário esférico no vácuo do aplicativo em teste. Passamos para a propriedade de tarefas um dicionário de métodos que o usuário chamará e sua frequência de chamadas. Agora, apesar de não sabermos qual função e em que ordem cada usuário chamará - eles são selecionados aleatoriamente, garantimos que a função de índice seja chamada em média 2 vezes mais frequentemente que a função de perfil .

Além do comportamento, a classe pai TaskSet permite especificar 4 funções que podem ser executadas antes e depois dos testes. A ordem das chamadas será a seguinte:

  1. setup - é chamado 1 vez no início do UserBehavior (TaskSet) - não está no exemplo
  2. on_start - chamado 1 vez por cada novo usuário da carga na inicialização
  3. tarefas - execução das próprias tarefas
  4. on_stop - chamado uma vez por cada usuário quando o teste termina de funcionar
  5. desmontagem - é chamado uma vez quando o TaskSet sai - também não está no exemplo

Vale ressaltar aqui que existem 2 maneiras de declarar o comportamento do usuário: o primeiro já está indicado no exemplo acima - as funções são anunciadas antecipadamente. A segunda maneira é declarar métodos diretamente dentro da classe UserBehavior :

 from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): def on_start(self): self.client.post("/login", {"username":"ellen_key", "password":"education"}) def on_stop(self): self.client.post("/logout", {"username":"ellen_key", "password":"education"}) @task(2) def index(self): self.client.get("/") @task(1) def profile(self): self.client.get("/profile") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 5000 max_wait = 9000 

Neste exemplo, as funções do usuário e a frequência da chamada são definidas usando a anotação da tarefa . Funcionalmente, nada mudou.

A última classe do exemplo é WebsiteUser (o nome da classe pode ser qualquer um). Nesta classe, definimos o modelo de comportamento do usuário UserBehavior *** +, bem como os tempos mínimo e máximo de espera entre as chamadas para tarefas individuais de cada usuário. Para deixar mais claro, veja como visualizá-lo:



Introdução


Execute o servidor, cujo desempenho iremos testar:

 json-server --watch sample_server/db.json 

Também modificamos o arquivo de exemplo para que ele possa testar o serviço, remover o logon e logout, definir o comportamento do usuário:

  1. Abra a página principal 1 vez no início dos trabalhos
  2. Obtenha uma lista de todas as postagens x2
  3. Escreva um comentário no primeiro post x1

 from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): def on_start(self): self.client.get("/") @task(2) def posts(self): self.client.get("/posts") @task(1) def comment(self): data = { "postId": 1, "name": "my comment", "email": "test@user.habr", "body": "Author is cool. Some text. Hello world!" } self.client.post("/comments", data) class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 1000 max_wait = 2000 

Para iniciar no prompt de comando, execute o comando

 locust -f my_locust_file.py --host=http://localhost:3000 

onde host é o endereço do recurso testado. É a ele que os endereços dos serviços indicados no teste serão adicionados.

Se não houver erros no teste, o servidor de carregamento será iniciado e estará disponível em http: // localhost: 8089 /



Como você pode ver, o servidor que testaremos é indicado aqui - é nesta URL que os endereços dos serviços do arquivo de teste serão adicionados.

Também aqui podemos indicar o número de usuários para a carga e seu crescimento por segundo.
No botão, iniciamos o carregamento!



Resultados


Após um certo tempo, paramos o teste e observamos os primeiros resultados:

  1. Como esperado, cada um dos 10 usuários criados no início foi para a página principal
  2. A lista de postagens foi aberta em média duas vezes mais do que um comentário foi escrito
  3. Há um tempo de resposta médio e mediano para cada operação, o número de operações por segundo já é um dado útil, mesmo que agora o pegue e compare com o resultado esperado dos requisitos

Na segunda guia, você pode ver os gráficos de carga em tempo real. Se o servidor travar a uma determinada carga ou se seu comportamento mudar, isso será imediatamente visível no gráfico.



Na terceira guia, você pode ver os erros - no meu caso, este é um erro do cliente. Mas se o servidor retornar um erro 4XX ou 5XX, seu texto será escrito aqui
Se ocorrer um erro no código do seu texto, ele cairá na guia Exceções. Até agora, tenho o erro mais comum relacionado ao uso do comando print () no código - essa não é a melhor maneira de registrar :)

Na última guia, você pode baixar todos os resultados dos testes no formato csv

Esses resultados são relevantes? Vamos descobrir. Na maioria das vezes, os requisitos de desempenho (se houver algum) são mais ou menos assim: o tempo médio de carregamento da página (resposta do servidor) deve ser menor que N segundos com uma carga de M usuários. Na verdade, não especificando o que os usuários devem fazer. E eu gosto de gafanhotos por isso - ele cria a atividade de um número específico de usuários que executam aleatoriamente as ações esperadas que eles esperam dos usuários.

Se precisarmos realizar uma referência - para medir o comportamento do sistema em diferentes cargas, podemos criar várias classes de comportamento e realizar vários testes em diferentes cargas.

Isso é o suficiente para começar. Se você gostou do artigo, pretendo escrever sobre:

  • cenários de teste complexos nos quais os resultados de uma etapa são usados ​​nas seguintes
  • processamento de resposta do servidor, como pode estar errado mesmo que o HTTP 200 OK tenha chegado
  • dificuldades óbvias que podem ser encontradas e como contorná-las
  • testando sem usar a interface do usuário
  • teste de carga distribuída

Source: https://habr.com/ru/post/pt430502/


All Articles