Um dos maiores benefícios para equipes que usam Node.js é a capacidade de usar JavaScript no cliente e no servidor. Mas como podemos garantir que não escreveremos código JavaScript não otimizado em Node.js? Além disso, como você testa o desempenho do seu aplicativo durante o desenvolvimento e depois de implantado na produção?

Neste artigo, veremos algumas práticas recomendadas para escrever e testar seu código Node.js e, em seguida, colocá-las em uso com alguns exemplos.

Práticas recomendadas de Node.js

Primeiro, vamos começar com algumas práticas recomendadas para incorporar ao seu código quando se trata de recursos da CPU.

Processos filho

Apesar de ser de thread único, o Node.js pode gerar processos filho para cuidar das tarefas. Embora útil quando usado com moderação, uma superabundância de processos filhos reduzirá drasticamente o desempenho do seu código. Um processo filho vem com sobrecarga em sua CPU, como consumo de memória, que usa os recursos limitados de sua máquina. Portanto, é importante não gerar mais processos filho do que núcleos em sua máquina.

Abaixo está um exemplo de um script Node.js gerando processos filho usando a variável cpuCount , que determina se é necessário ou não gerar mais processos filho:

 var counter=0;
fn: assíncrono (entradas, saídas) { const os=require ('os') //Obtenha o número de núcleos de CPU const cpuCount=os.cpus (). comprimento if (contador> cpuCount) lança um novo erro ('Não é possível gerar mais processos filho') var fork=require ('child_process'). fork; var child=fork ('./test'); console.log ('Teste iniciado....'); contador ++;//observe esta linha
}

Threads de trabalho

Trabalhadores ou trabalhador threads , são úteis para executar operações JavaScript com uso intensivo de CPU.
No entanto, não os use para operações intensivas de entrada/saída. Node.js lida com entrada/saída assíncrona de forma mais eficiente do que você pode com trabalhadores, conforme descrito na documentação do Node.js .

No trecho de código abaixo, estamos criando uma instância do construtor Worker quando estamos no thread principal do aplicativo. Posteriormente, realizaremos tarefas com uso intensivo de CPU no bloco else e passaremos uma mensagem de volta ao thread principal com parentPort.postMessage ('Hello world!') :

 const {Worker, isMainThread, parentPort}=require ('worker_threads');
if (isMainThread) { //Este código é executado no thread principal e não no trabalhador. //Cria uma instância de Worker trabalhador const=novo trabalhador (__ nome do arquivo); //Ouça as mensagens do trabalhador e imprima-as. worker.on ('mensagem', (msg)=> {console.log (msg);});
} senão { //Este código é executado no trabalhador e não na thread principal. //Envie uma mensagem para o tópico principal. //Aqui você executa tarefas que exigem muita CPU parentPort.postMessage ('Olá, mundo!');
}

PM2

PM2 é um gerente de processo para aplicativos Node.js de produção. Ele auxilia no dimensionamento de aplicativos da web em vários processos locais, melhora o desempenho de seu aplicativo e também ajuda no monitoramento. Instale o PM2 executando o seguinte comando em seu terminal:

 npm install-g pm2

Inicie seu aplicativo executando o seguinte código, em que app.js é o ponto de entrada:

 pm2 start app.js

PM2 permite que você aproveite as vantagens de sistemas multi-core, executando seu aplicativo em clusters, tornando-o fácil de escalar seu aplicativo para cima ou para baixo. Podemos dizer ao PM2 para executar nosso aplicativo em duas instâncias:

 pm2 start app.js-i 2

Lembre-se de que o PM2 é executado em segundo plano, o que significa que você pode facilmente esquecer de fechá-lo quando não estiver usando. Adicione o seguinte código para ver quais processos estão em execução:

lista

 pm2

Feche o processo em execução usando:

 pm2 matar

Limites de memória

O Node.js aloca memória suficiente para manter todos os objetos no escopo atual. Por padrão, a memória é limitada a 512 MB. Conforme o consumo de memória se aproxima de seu limite, o V8 gastará mais tempo na coleta de lixo, o que, por sua vez, afetará o desempenho do seu aplicativo.

Você pode aumentar o limite de memória com a opção --max-old-space-size . O --max-old-space-size é uma opção Node.js V8 CLI que permite que você substitua o tamanho de memória padrão.

Para usar a opção --max-old-space-size , passe-a como uma opção ao iniciar o Node.js e dê a ela um tamanho em megabytes. Por exemplo, o snippet de código abaixo aumentará o tamanho da memória para 1536 MB (1,5 GB):

 node--max-old-space-size=1536 index.js

Reutilizando objetos

A maioria dos objetos em um aplicativo Node.js tem um ciclo de vida curto, portanto, é melhor reutilizar os objetos sempre que possível. Criar muitos objetos novos tornará seu aplicativo lento porque você terá que executar o lixo Node.js colecionador com frequência.

Para verificar o uso de memória em seu aplicativo, você pode usar o método process.memoryUsage () :

 process.memoryUsage (). heapTotal//Obtém toal de heap de memória disponível em bytes
process.memoryUsage (). heapUsed//Obtém a pilha de memória usada em bytes

É importante evitar a execução do coletor de lixo na produção porque ele registrará constantemente os valores da execução dessas duas funções em seu ambiente de registro de produção. Esse processo é, no entanto, adequado para um ambiente de desenvolvimento. Você pode monitorar o uso de memória em seu aplicativo em Chrome Developer Tools (DevTools) no Seção de guias de memória .

Teste de carga

Depois de construir seu aplicativo Node.js, é uma boa ideia realizar um teste de carga. Um teste de carga determina o número de solicitações que seu aplicativo pode manipular com eficiência por vez e como o aplicativo se comportará com solicitações grandes. Também o ajudará a decidir como dimensionar seu aplicativo. Para teste de carga, você pode usar o Apache JMeter.

Apache JMeter

De acordo com seu site, Apache JMeter é um aplicativo Java puro de código aberto projetado para carregar o comportamento funcional de teste e medição de desempenho. Você pode baixar o JMeter e seguir o Guia de primeiros passos para usá-lo no teste de carga.

Com o Apache JMeter, você pode registrar qualquer comportamento em seu aplicativo Node.js, como uploads de arquivos e sessões de cliente. Você também pode fazer solicitações em lote para testar a carga do seu aplicativo e, em seguida, observar e monitorar os gargalos de desempenho.

Exibição do plano de teste ApacheJmeter
Apache JMeter

Agora, você conhece algumas práticas recomendadas para criar aplicativos Node.js. No entanto, você ainda deve verificar se o seu código tem o melhor desempenho possível. Na próxima seção, veremos algumas análises de desempenho para Node.js.

Comparativo de desempenho do Node.js

Um aplicativo Node.js lento geralmente é o resultado da falta de recursos. Embora existam outros recursos, como a rede e o disco rígido, vamos nos concentrar nestes dois:

  1. Processador
  2. Memória

O Node.js sai da caixa com várias ferramentas que permitem monitorar a utilização de recursos.

Inspetor Node.js

Quando você inicia seu aplicativo Node.js com a opção --inspect ou --inspect-brk , o processo Node.js escuta um cliente de depuração. Ele permite que você depure seu código Node.js usando uma ferramenta com a qual você está familiarizado para depurar JavaScript do lado do cliente, que são as ferramentas Chrome Dev em nosso caso.

Para usar o Inspetor, execute seu script Node.js com o comando node---inspect-brk app.js , que irá:

  • Ative o Inspetor Node.js
  • Vincule o inspetor ao endereço IP: 127.0.0.1
  • Ouça na porta 9229
  • Interrompa antes que o código do usuário comece

Depois de executar o comando, vá para o navegador Google Chrome. Ele executa o mesmo mecanismo de JavaScript do Node.js, o mecanismo V8. Digite about: inspect na barra de endereço e ele o redirecionará para chrome://inspect .

Nodejs Inspector Chrome Browser Display

Para depurar sua sessão Node.js, clique no link inspecionar para abrir uma janela pop-up com as ferramentas de desenvolvedor do Chrome. Se você já sabe como usar as ferramentas de desenvolvedor do Chrome, pode prosseguir para criar o perfil de seu aplicativo e ver o consumo de recursos. Você pode ler mais sobre depuração de JavaScript no Chrome .

Chrome Developer Tools Debugger
Chrome Depurador

Threads de execução

Conforme mencionado anteriormente, é importante lembrar que o Node.js é single threaded, o que significa que você tem apenas um processo de execução para trabalhar. Imagine milhões de clientes trabalhando com aquele único segmento! Esse é um recurso muito limitado.

Embora essa abordagem de thread único mantenha o Node.js simples, para que não tenhamos que lidar com coisas como Thread safety , ele simplesmente não escala.

O Node usa entrada/saída assíncrona para agendar operações de leitura e gravação, o que não bloqueia o processo de execução. No entanto, se seu código for escrito sem seguir as práticas recomendadas, ele é capaz de usar 100 por cento da CPU e bloqueará o processo de execução do Node.js.

Vejamos um exemplo de código que pode bloquear o thread de execução:

 função blockProcess () { while (true) { console.log ("Processo de bloqueio") }
}

Embora este seja um exemplo superficial, se você executar a função blockProcess e monitorar sua CPU, verá como isso aumenta o consumo de CPU para quase 100 por cento. Portanto, é inteligente evitar escrever código que bloqueie processos como este.

Medir a carga da CPU

Você precisará medir a carga da CPU para analisar se você tem um problema de alta carga da CPU. Uma maneira de verificar é usando as ferramentas de desenvolvedor do Chrome com a sessão de processo Node.js.

Vá para a seção ferramenta de criação de perfil das Ferramentas do desenvolvedor do Chrome, que fornece um perfil de CPU que informa quanto tempo leva uma determinada função, subprocesso ou recurso. Em seguida, procure um arquivo que tenha um alto consumo de CPU. Clique nele para descobrir qual função/instrução está causando a alta carga da CPU para que você possa trabalhar na correção do problema.

Medir a seção da ferramenta de perfil de carga da CPU
Criação de perfis de CPU no Chrome

Tempo de execução

O tempo de execução é a quantidade de tempo que alguns blocos do nosso aplicativo levam para serem executados do início ao fim. Para medir o tempo de execução, envolva o bloco de código que deseja medir usando o
Métodos console.time e console.timeEnd . Por exemplo, o código abaixo está fazendo uma solicitação de rede em um Sails aplicativo da web.js :

 fn: função assíncrona (entradas, saídas) {
var wavesNGNPrice=await sails.helpers.fetch ('https://waves-africa-api.herokuapp.com/ngn');
exits.success (wavesNGNPrice)
}

A implementação da função não é tão importante para o que queremos alcançar agora, mas digamos que queremos saber quanto tempo a primeira linha da função leva para ser executada. Nós o embrulharíamos assim:

 fn: função assíncrona (entradas, saídas) {
console.time (‘em espera’)
var wavesNGNPrice=await sails.helpers.fetch ('https://waves-africa-api.herokuapp.com/ngn');
console.timeEnd (‘em espera’) exits.success (wavesNGNPrice)
}

Quando iniciarmos o aplicativo, veremos uma saída no console exibindo a quantidade de tempo em milissegundos que o código leva para ser executado. Podemos usar essa saída para ver o que pode estar bloqueando nosso código e tornando o aplicativo lento.

Sails de medida de tempo de execuçãoj s

Ganchos de desempenho

Outra ferramenta Node.js para medir o desempenho é o perf_hooks , que permite medir o desempenho de diferentes aspectos de seu aplicativo ao criar marcadores em seu código.

Vamos ver isso em ação, usando-o para medir o mesmo bloco de código que fizemos acima. Primeiro, precisamos importar PerformanceObserver e performance do módulo perf_hooks :

 const {PerformanceObserver, performance}=require (‘perf_hooks’)

A seguir, criaremos um novo objeto observador:

 const obs=new PerformanceObserver (items=> { console.log (items.getEntries () [0].name, items, getEntries () [0].duration);
performance.clearMarks ();
});

Em seguida, chamaremos o método observador no objeto, dizendo quais tipos de entrada queremos:

 obs.observe ({entryTypes: [‘medir’]});

Finalmente, envolveremos o código com o método performance.mark e produziremos a medição executando performance.measure :

 fn: função assíncrona (entradas, saídas) { performance.mark (‘começar a esperar’)
var wavesNGNPrice=await sails.helpers.fetch ('https://waves-africa-api.herokuapp.com/ngn');
performance.mark ('finalizar a espera') performance.mark (‘período de espera’, ‘início de espera’, ‘fim de espera’) exits.success (wavesNGNPrice)
}

Você deve obter uma saída semelhante à mostrada abaixo:

Performance Hooks Observer Method Output

Na implementação acima do PerformanceObserver , estamos registrando o tempo de execução no console. A documentação do Node.js em APIs de medição de desempenho será útil para obter um entendimento mais profundo dessa funcionalidade.

O módulo perf_hooks não tem efeitos colaterais, portanto, ele não mudará nada quando você colocá-lo em seu código. É útil quando você suspeita que uma linha específica é a culpada pelo estado lento do seu aplicativo Node.js.

Se você estiver tendo problemas de desempenho, o observador perf_hooks pode ser incluído na produção para medir a análise de desempenho em um projeto ao vivo. No entanto, você não deve ativá-lo em todos os casos na produção, pois pode causar atrasos no seu aplicativo.

Conclusão

Vamos revisar as práticas recomendadas que abordamos para medir o desempenho do seu código.

  • Teste com dados ao vivo: simule um estado do mundo real para seu aplicativo em vez de usar dados fictícios
  • Execute testes de carga regulares: verifique se os novos recursos aumentam o desempenho do seu sistema
  • Predefina suas métricas de desempenho e observe-as: saiba o que você está testando e observando de antemão

Como o desempenho de nosso aplicativo Node.js afeta nossos usuários diretamente, um bom desenvolvedor sabe que deve garantir que os aplicativos tenham um bom desempenho e sejam otimizados!

A postagem Práticas recomendadas do Node.js. e análise de desempenho em 2021 apareceu primeiro no LogRocket Blog .