Criando na prática threads no node.js com worker_threads
100
0

Criando na prática threads no node.js com worker_threads

Descubra como criar threads no node.js na prática. Veja também, qual é o problema que as worker_threads resolve no node.js, e quando devemos ou não usá-las.

Leonardo Brito04/15/2021
4 min
100
0
Email image

Eu tive a curiosidade de estudar um pouco mais sobre a worker_threads do node.js e gostaria de compartilhar com vocês o que eu aprendi, a ideia desse artigo é trazer uma noção de tudo que estudei até hoje e compartilhar com vocês.

Nada melhor do que começar com a definição de threads:

Thread é uma forma de um processo dividir a si mesmo em duas ou mais tarefas que podem ser executadas de maneira concorrentes.

Uma vez que temos uma breve noção do que são threads, agora vamos para parte que eu mais gosto, a PRÁTICA 😎

Imagine que seu código deseja executar uma leitura no disco, como por exemplo, ler um arquivo json gigantesco e logo em seguida ele irá rodar uma função matemática síncrona muito pesada. Veja o código na imagem abaixo:

Imagem 1 - Trecho do código
Imagem 1 - Trecho do código

Uma vez que foi apresentado o código para você, precisamos recapitular como o event loop do node.js funciona.

Entendendo o event loop do node.js

Quando o node.js executar a linha 9, ele irá executar as seguintes etapas:

  1. Enviar a função fs.readFile para a task queue;
  2. O event loop pegará a função fs.readFile da task queue e irá mandar para a thread principal do node.js, a stack;
  3. Uma vez que a função fs.readFile é uma função assíncrona, ele irá delegar a resolução desse método para sua background thread - a libuv - e dará sequência nas próximas linhas do trecho do código;
  4. Chegando na linha 13, o node.js irá enviar a função fibonnaci para a task queue, e o event loop por sua vez, irá resgatar essa função da task queue e irá enviar essa função para a thread principal, a stack

Uma animação para ficar mais claro para nós 🤩

Animação 1 - Simulação do Event Loop do Node.js
Animação 1 - Simulação do Event Loop do Node.js

Quais sãos os problemas que a worker_threads resolve?

Funções síncronas que demandam muita CPU

A worker_threads irá rodar de maneira paralela funções síncronas sem "travar" a Stack - a thread principal do node.js

Quando devemos usar as worker_threads?

O uso da worker_threads é recomendado nos seguintes cenários:

  1. Cálculos matemáticos pesados
  2. Funções de analytics
  3. Funções de Machine Learning
  4. Parse de JSON gigantescos
  5. Parses e Compilers, por exemplo, o typescript
  6. Test Runners, por exemplo, o Jest

Quando não devemos usar as worker_threads?

O node.js trabalha muito bem com I/O através da libuv - background threads - ou seja, é muito custoso e não há necessidade de worker_threads para executar funções como:

  1. Ler arquivos de disco
  2. Request HTTP

🔥 PRÁTICA: Codificando uma worker_thread em node.js

No exemplo abaixo eu já tenho um projeto node.js configurado com o express e um endpoint para teste.

Imagem 3 - Arquivo index.js
Imagem 3 - Arquivo index.js

Primeiramente, eu crio um arquivo separado chamado worker.js que será responsável por abstrair toda a configuração de um Worker.

Imagem 4 - Arquivo worker.js
Imagem 4 - Arquivo worker.js

Na linha 1  - Eu tenho o import da lib worker_thread.

Na linha 5 - Eu tenho eu crio uma instância nova da classe Worker e o primeiro parâmetro que devo informar é qual o arquivo eu desejo rodar em uma nova thread, o segundo parâmetro são algumas opções, e vamos focar apenas na opção workerData - responsável por passar dados para o arquivo que irá rodar em uma nova thread.

Na linha 6, 7, e 8 - Nessas linhas eu estou configurando algumas callbacks quando determinados eventos forem disparados pela Worker, como por exemplo, message em caso de sucesso, error, e exit.

Na linha 12 - Nessa linha eu estou imprimindo no terminal o threadId para eu conseguir localizar qual thread está rodando.

Depois, eu chamo a minha função startNewWorkerPromised criada no arquivo worker.js no meu arquivo index.js onde possui a implementação do meu endpoint para teste.

Imagem 5 - Arquivo index.js com a implementação da worker_thread
Imagem 5 - Arquivo index.js com a implementação da worker_thread

Na linha 11 - Eu defino o caminho do arquivo onde possui a minha função matemática pesada.

Na linha 12 - Eu configuro os dados importantes que desejo passar para a minha função pesada.

Na linha 14 - Eu chamo a minha função startNewWorkerPromised - implementada anteriormente - e passo o caminho do arquivo e os dados necessários, por fim, eu implemento o then e o catch  da minha promise para eu obter o resultado.

Obs.: Assim que o event loop executar a função startNewWorkerPromised ele não irá travar a thread principal - a Stack - pois irá delegar para a background thread (libuv) e continuará executado os próximos códigos do arquivo index.js, que no caso é a linha 18 que responde a requisição do usuário com uma mensagem.

Agora vamos ver o nossa função pesada, no arquivo math.js:

Imagem 6 - Arquivo math.js
Imagem 6 - Arquivo math.js

Na linha 1 - Nessa linha eu estou importando o workerData, para capturar os dados passados no momento da instância do novo Worker, e o parentPort é uma porta de comunicação com o pai do Worker no caso a thread principal.

Na linha 3 até 7 - Nessa linha é a implementação da nossa função pesada.

Na linha 9 - Nessa linha eu extraio a propriedade number que eu passei para o workerData como uma nova constante .

Na linha 10 - Nessa linha eu chamo a função fibonacci e passo o number como parâmetro para a função executar.

Na linha 12 - Nessa linha eu envio um objeto com o number e o result da função para o pai do Worker, no caso a thread principal.

FINALMENTE, bora testar essa implementação, então primeiro eu rodo o meu servidor express, e eu irie fazer 5 chamadas para o meu endpoint /worker passando um parâmetro number = 44

Animação 2 - Testando implementação <i>worker_threads</i>
Animação 2 - Testando implementação worker_threads

Como você pode notar na animação 2, logo que eu faço a requisição em poucos milissegundos eu já obtenho a resposta "Starting new Node.js Worker..." e no terminal é impresso o threadId que foi criada pela worker_threads

Após alguns minutos... o resultado

Animação 3 - Resultado calculado pelas threads instanciadas
Animação 3 - Resultado calculado pelas threads instanciadas

Pronto! Cálculo pesado processado em segundo plano paralelamente com threads no node.js sem travar a nossa tão querida Stack. ❤️

Considerações finais

Pessoal, esse foi o meu primeiro artigo, espero que tenham gostado, o objetivo dele é compartilhar os estudos que eu venho fazendo sobre a worker_threads do node.js, e espero que você saia desse artigo com uma noção clara do que é e como utilizá-la em seus projetos.

Deixe me saber se realmente gostou e avalie, compartilhe, me siga nas redes sociais, dê o seu feedback, sugira novos artigos!

Support
More Options