Como fazer testes de carga com Python + Locust.io

Thiago Ferreira
WhatsGood Dev
Published in
5 min readNov 4, 2021

Dentro do contexto de sistemas de alta performance e escalabilidade,
teste de carga é um tipo de teste de performance, onde verificamos como um determinado sistema lida com muitos acessos simultâneos por um determinado período de tempo.

Quando usar esse tipo de teste?

Existem diversos momentos onde podemos utilizar testes de carga:
- Ao fazer melhorias de performance, podemos checar se as melhorias realmente são efetivas.
- Ao tomar decisões sobre qual stack de tecnologia usar, os testes de carga podem ser usados para comparar múltiplas abordagens e decidir qual é a mais apropriada.
- Estimar a quantidade de usuários que um determinado sistema suporta.
- Encontrar gargalos no projeto, ou seja, componentes que acabam demorando muito pra responder e afetam o tempo de resposta como um todo.

Conhecendo a ferramenta locust.io

Locust.io é uma ferramenta open source, feita em python, para executar testes de carga. O comportamento do usuário é definindo por código python puro, então é totalmente customizável e extensível para diversos usos.

Configurando o teste de carga

É possível rodar esse projeto de exemplo tanto na sua máquina local, quanto subir o projeto em algum cloud provider pra rodar os testes mais próximos da realidade. Pra começar, vamos precisar desses dois repositórios:

  • locust-stress-test: Implementação da ferramenta locust.io, onde é definido o comportamento do usuário, como as páginas acessadas e a frequência
  • django-todo-list: Uma api simples feita em python/django, que será o alvo do teste de carga.

O código do locust ficará assim:

Estamos basicamente configurando um comportamento de usuário que irá:

  • Ver a listagem de tarefas com `GET /tasks/`;
  • Criar novas tarefas com `POST /tasks/`;
  • Cada usuário simulado irá esperar entre 1 e 3 segundos entre requisições;
  • A proporção de acesso na listagem é 5x maior que a criação de novas tarefas. Isso simula o comportamento de que cada usuário geralmente irá ler muito mais dados do que escrever.

Montando o Ambiente

É preferível sempre rodar o teste de carga numa cloud (AWS, DigitalOcean, etc) para ter resultados mais próximos da realidade já que evita-se, assim, interferência da sua máquina local nos testes. Para isso, escolhi os seguintes recursos para esse exemplo:

Executando o teste

As instruções de instalação e execução estão no repositório locust-stress-test, mas em resumo o comando é o seguinte:

poetry run locust --host=http://localhost:8000

Após isso, abra a interface web mostrada pelo retorno do comando e configure os parâmetros de testes e clique em “Start swarming”:

Resultados e conclusões

Abaixo temos o relatório completo do locust.io. Alguns pontos importantes:

  • Até cerca de 160 usuários simultâneos, estavamos fazendo cerca de 60 requests por segundo e o tempo de resposta estava aceitável para esse contexto (95% das requisiçoes terminavam em menos de 400ms).
  • A definição de tempo de resposta aceitável pode variar de contexto para contexto. Geralmente, buscamos ter um tempo de resposta menor que 300ms pra APIs em geral, mas lembrando que isso é variável dependendo do contexto de cada produto.
  • Depois de passar dos 160 usuários, repare que o número de requests por segundo continuou o mesmo (60rps), mas o tempo total de resposta aumentou muito. Com isso, cada usuário passou a receber respostas demorando vários segundos.
  • Em resumo, com os recursos disponíveis, conseguiríamos atender cerca de 160 usuários ou 60rps com um tempo de resposta abaixo de 400ms. Para aumentar essa quantidade, precisaríamos de mais recursos computacionais ou ajustes na performance do código.

Confira o relatório completo:

Entendendo o uso de CPU e memória

Aplicação

Na aplicação, podemos ver o uso CPU crescendo até chegar em 100%. De certo modo, é um bom sinal que o CPU esteja subindo, isso quer dizer que o servidor de aplicação está com as configurações corretas e não está deixando recursos ociosos.

De outro lado, como o CPU está chegando em 100%, isso quer dizer que já precisaríamos de mais recursos computacionais.

Banco de dados

Aqui também podemos ver um alto uso de CPU. Isso também mostra que os recursos do nosso banco de dados também podem ser aumentados. Temos duas abordagens aqui:

  • Aumentar os recursos do banco (+ CPU, + Memória)
  • e/ou Criar réplicas de leitura, assim teremos pelo menos uma instância de leitura e uma de escrita
  • e/ou colocar cache (redis, memcached, etc) onde for possível para diminuir o acesso ao banco de dados.

Uma mudança de cada vez

Ao fazer testes de carga, é muito importante fazer pequenas mudanças, uma de cada vez. Assim podemos verificar quando uma mudança melhorou ou piorou os resultados. No screenshot abaixo, podemos ver o ponto exato onde eu alterei uma configuração e o tempo de resposta melhorou muito:

Aliando as pequenas mudanças com o monitoramento dos resultados e dos recursos computacionais, conseguimos entender como nosso sistema se comporta e melhorá-lo de acordo.

Além disso, essa é uma excelente forma de aplicar o Princípio da falseabilidade em tecnologia e com isso tomar decisões mais assertivas sobre nossas aplicações e infraestrutura.

Leituras Adicionais

--

--