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.
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: | ||
| ||
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: | ||
| ||
Uma animação para ficar mais claro para nós 🤩 | ||
| ||
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: | ||
| ||
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: | ||
| ||
🔥 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. | ||
| ||
Primeiramente, eu crio um arquivo separado chamado worker.js que será responsável por abstrair toda a configuração de um Worker. | ||
| ||
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. | ||
| ||
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: | ||
| ||
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 | ||
| ||
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 | ||
| ||
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! |