A performance de aplicações Node.js pode ser um fator crítico para escalabilidade e eficiência. Neste artigo, vamos explorar três ferramentas poderosas para análise de desempenho: console.time, CPU Profile nativo do Node.js e clinic.js.
A ideia não é explorar a fundo as métricas geradas pelas ferramentas, já que isso vai ser mutável de acordo com sua aplicação. Vou apresentar as ferramentas e te mostrar como implementar, assim você pode aplicar no seu cenário.
1. Identificando Problemas com console.time
Partindo do básico, conseguimos identificar pontos de lentidão no nosso código apenas marcando o tempo que cada função demora para ser executada. Imagine seu endpoint que conta com um middleware de autenticação, uma consulta no banco, um loop que opera algum calculo, etc. Podemos adicionar um console.time em cada um desses pontos críticos e depois verificar qual demora mais.
No exemplo abaixo só temos uma função, um cálculo fibonacci feito da forma mais lenta que eu consegui. Executando o script com um parâmetro de 40. Medindo o tempo temos um resultado de ~3 segundos.
function slowFibonacci(n) { if (n <= 1) return n; return slowFibonacci(n - 1) + slowFibonacci(n - 2); }
A função acima é lenta pois é recursiva e cresce exponencialmente O(2^n), se mudarmos essa complexidade para que ela se torne linear O(n) o tempo de execução cai drasticamente e vamos ao tempo de ~0.023ms.
function fastFibonacci(n, memo = {}) { if (n in memo) return memo[n]; if (n <= 1) return n; memo[n] = fastFibonacci(n - 1, memo) + fastFibonacci(n - 2, memo); return memo[n]; }
2. Gerando um CPU Profile.
O Node.js possui uma API interna chamada inspector que permite capturar um CPU Profile, útil para identificar funções que consomem mais tempo de CPU.
A função abaixo vai encapsular o nosso código, assim podemos reutilizar para qualquer endpoint.
function generateProfile(res, callback) { const session = new Session(); session.connect(); session.post('Profiler.enable', () => { session.post('Profiler.start', () => { callback((result) => { session.post('Profiler.stop', (err, { profile }) => { writeFileSync('cpu-profile.json', JSON.stringify(profile)); res.send(`Resultado: ${result}. CPU Profile gerado.`); }); }); }); }); }
Implementado o gerador de cpu-profile no endpoint problemático.
app.get('/profile', (req, res) => { generateProfile(res, (done) => { const result = slowFibonacci(40); done(result); }); });
Eu sempre utilizo a ferramenta CPUPRO, com ela fica muito mais simples analisar o comportamento da aplicação do início ao fim.
3. Analisando Performance com clinic.js
O clinic.js é uma ferramenta que fornece insights sobre o desempenho da aplicação de forma automatizada. Para utilizá-lo:
Instale o pacote:
npm install -g clinic
Execute a análise da aplicação:
clinic doctor -- node server.js
A ferramenta gerará um relatório visual com sugestões de otimização.
Extra: autocannon para testes de carga
Em todos os casos podemos gerar os relatórios apenas fazendo uma requisição simples com alguma ferramenta ou até com o navegador. Mas em um cenário real não vamos ter apenas um cliente acessando nossa API, se seu produto for um sucesso no seu bairro e 100 pessoas resolverem acessar ao mesmo tempo, ele suportaria a carga?
Nós só podemos responder a essa pergunta fazendo um teste de carga, e tem várias formas de fazer isso. No nosso caso vamos manter os testes em ambiente local e usar uma ferramenta chamada autocannon. Pra te ajudar nisso eu vou deixar um script pronto que você pode usar na sua aplicação, lembre-se de instalar o autocannon antes.
npm i -g autocannon
cannon.sh
#!/bin/bash BASE_URL="http://localhost:3000" # URL base do servidor DURATION="10" # Duração do teste em segundos CONNECTIONS="10" # Número de conexões simultâneas URL=$BASE_URL/$1 # URL do endpoint a ser testado autocannon -d $DURATION -c $CONNECTIONS $URL
Neste caso estamos fazendo 100 clientes se conectarem por um período de 10 segundos, esses parâmetros e outros podem ser editados da forma que você preferir.
Executando nosso script vamos ao resultado:
Se você não tem muita intimidade com essa ferramenta vou te dar uma dica, olhe para as requisições por segundo, se seu código está bem otimizado você vai ter muitas reqs/sec.
Se seu código está com problemas, você pode olhar para essa métrica para verificar se suas correções estão surtindo efeito.
Conclusão
Eu te mostrei algumas formas de testar e medir performance do seu código no Node.js, mas sabe qual a melhor delas?
Isso vai depender do problema que você está enfrentando, se for algo simples você vai descobrir apenas usando o console.time, se for mais complexo você talvez use o clinic.js para observar as métricas ou o CPU profile nativo para verificar se alguma biblioteca externa está consumindo muitos recursos.
E em todos os casos você pode fazer testes com o Autocannon para verificar se seu número de requisições por segundo está melhorando.