Boas-vindas ao curso de Gerenciamento de Threads e Processos com o Node.js! Eu sou o Thiago Bussola e serei seu instrutor.
Audiodescrição: Thiago é um homem branco de cabelos e barba curtos na cor castanho-escuro. Usa óculos de grau com armação quadrada e uma camiseta preta. Ao fundo, há uma estante com livros e objetos de decoração.
Este curso é destinado a quem já possui experiência no desenvolvimento de aplicações em Node.js e deseja aprender a criar scripts que utilizem melhor a concorrência e o paralelismo. Além de entender como funciona o loop de eventos e o ciclo interno do Node.js.
Neste curso, você aprenderá a usar strings para ler e inserir dados no banco de dados, além de entender como funciona o loop de eventos no Node.js.
Vamos explorar o uso de threads e processos, aprendendo a utilizar worker threads para tarefas intensivas de CPU. Também veremos como usar o módulo Spawn para chamar subprocessos e o Child Process para inserir informações no banco de dados de maneira eficiente.
Para tirar o máximo proveito deste conteúdo, é importante que você tenha concluído o curso anterior sobre Node.js. Você deve ter experiência em desenvolver APIs REST com NEST ou outro framework e um conhecimento básico sobre manipulação de arquivos com Node.js.
Aproveite os recursos da plataforma, que incluem vídeos, atividades, e o apoio do fórum e da nossa comunidade no Discord. Vamos estudar!
Neste vídeo, vamos criar uma base de dados para trabalharmos ao longo do curso. Para isso, é necessário que você tenha realizado a atividade Preparando Ambiente e que tenha configurado um banco de dados Postgres. Isso pode ser feito com Docker ou com o Postgres instalado localmente, além de um gerenciador de banco de dados, como o Postgres, Beekeeper ou MySQL Workbench.
Com tudo preparado, vamos criar uma tabela que simulará, por exemplo, a tabela de usuários de um e-commerce ou de outro site que possua uma tabela de usuários. Nesse caso, já temos tudo pronto. Na nossa model, podemos ver todos os campos que essa tabela de usuários contemplará, desde o campo de senha até o nome da pessoa.
Para começar, acessamos a raiz do projeto e na pasta "src", clicamos com o botão direito e depois tem "New Folder" para criar uma nova pasta chamada "seed". Nessa pasta criamos um arquivo chamado seed.ts
. Nele, faremos alguns imports necessários para a conexão com o banco de dados e o modelo de usuário, começando com o connectDB
e o closeDB
. Depois, importamos o User
e o faker
.
import { closeDB, connectDB } from "../db/connection";
import User from "../db/user.model";
import { faker } from "@faker-js/faker";
Criamos uma função generateUser()
, que retornará um objeto gerado com o faker. Este objeto incluirá todos os campos presentes na model. Por exemplo, o campo name
será gerado com faker.internet.username
, e o campo company
com faker.company.name
.
Lembrando que é importante que os nomes dos objetos sejam iguais aos que estão na model.
Na linha abaixo passamos o dateBirth: faker.date.past()
, seguido de password
, que nesse caso receberá um faker
. O faker
tem uma propriedade para simular senhas, mas para as atividades futuras, queremos que estejam com o texto puro para que depois possamos criptografá-las. Então, passamos faker.internet.username() + faker.company.name()
.
Na sequência, escrevemos createdAt: faker.date.past({})
, abrindo função e objeto. Podemos passar alguns parâmetros, por exemplo, queremos os usuários criados até dez anos atrás, então passamos years: 10, refDate: new Date()
.
Feito isso, copiamos as três ultimas linhas de código que criamos e colamos logo abaixo. Feito isso, mudamos de createdAt
para updatedAt
e mudamos os anos para 9
. Fazemos o mesmo abaixo, mudando para lastPasswordUpdateAt
, ficando da seguinte forma:
function generateUser() {
return {
name: faker.internet.username(),
company: faker.company.name(),
dateBirth: faker.date.past(),
password: faker.internet.username() + faker.company.name(),
createdAt: faker.date.past({
years: 10,
refDate: new Date(),
}),
updatedAt: faker.date.past({
years: 9,
refDate: new Date(),
}),
lastPasswordUpdateAt: faker.date.past({
years: 8,
refDate: new Date(),
}),
};
}
Agora, vamos criar nossa função para inserir esses usuários. A função será async function seedUsers({})
. Podemos utilizar um bloco try-catch
para tratar possíveis erros durante a inserção.
async function seedUsers() {
try {
for (let i = 0; i < 200_000; i++) {
const user = generateUser();
await User.create(user);
}
console.log("Usuários inseridos")
} catch (error) {
console.error("Erro ao cadastrar usuários", error);
}
}
Para executar nossa função, criaremos uma closure, que é uma função autoexecutável que chamará outras funções do nosso código. Ela conectará primeiro, depois inserirá no banco e, por fim, fechará a conexão. Passamos (async () => {}
, depois, dentro da função, escrevemos await connectDB()
.
Para medirmos o tempo de execução, passamos console.time("seed-db")
, seguido da função await seedUsers()
. Por fim, passamos console.timeEnd("seed-db")
e await closeDB()
.
(async () => {
await connectDB();
console.time("seed-db");
await seedUsers();
console.timeEnd("seed-db");
await closeDB();
})();
Vamos salvar, abrir o terminal e executar o comando para rodar o script. Como estamos usando TypeScript, o comando será:
npx ts-node-dev src/seed/seed.ts
Utilizamos o Beekeeper como gerenciador de banco de dados. Vamos atualizar e verificar a tabela de usuários, onde os usuários estão sendo inseridos. A tabela já foi criada. Vamos rodar um script, que estará disponível na descrição do vídeo, para contar quantos usuários estão sendo inseridos.
SELECT COUNT(*) AS total_records FROM users
Agora, 200 mil usuários foram inseridos. No VS Code, notamos que a execução demorou 2 minutos e 24 segundos, sendo 2 minutos e 23 para inserir os registros no banco. Isso ocorre porque estamos inserindo usuários sequencialmente, um de cada vez. Precisamos esperar uma promessa ser resolvida antes de inserir no banco. No próximo vídeo, vamos explorar algumas abordagens para melhorar o tempo de inserção.
Como vimos no vídeo anterior, utilizamos uma abordagem mais interativa. Chamamos a função awaitUserCreate
200 mil vezes, o que demorou dois minutos. Se aumentarmos para um milhão de usuários, pode demorar de 15 a 16 minutos. Podemos ajustar o número de usuários conforme necessário, mas é possível melhorar o tempo de inserção para criar nossa base de dados.
Vamos abrir o Beekeeper Studio e remover a tabela atual, pois vamos gerar outra. Em seguida, voltamos ao código e comentamos da linha 35 até a linha 26. Na linha 35, quebramos uma linha duas vezes e, na linha 37, colamos a função novamente. Agora, faremos algumas pequenas modificações para melhorar o tempo de escrita.
Para melhorar a eficiência, precisamos modificar a abordagem. Na linha 37, dentro da função, criamos uma constante chamada constBatchSize
, que representa o tamanho do lote. Queremos inserir os dados em lotes de mil usuários.
const batchSize = 1000;
Assim, o for
continua o mesmo, exceto pela última parte, onde o i
incrementa de mil em mil até chegar aos 200 mil.
for (let i = 0; i < 200_000; i += 1000)
Promise.all
Podemos apagar as linhas 42 e 43 para deixar o for
mais limpo. Em seguida, criamos constBatchRecebeArray.from
, especificando que o array terá o tamanho do lote. Como parâmetro, passamos uma função anônima, generateUser
, para que cada índice do array seja um usuário gerado pela função criada anteriormente.
Chamamos awaitPromise.all
e passamos batch.map
, com uma função anônima que chama user.createUser
para cada usuário específico. Podemos adicionar um console.log
para mostrar o tamanho do lote inserido, usando interpolação para exibir "usuários inseridos".
async function seedUsers() {
const batchSize = 1000;
try {
for (let i = 0; i < 200_000; i += 1000) {
const batch = Array.from({ length: batchSize }, () => generateUser());
await Promise.all(batch.map((user) => User.create(user)));
console.log(`${i + batchSize} usuarios inseridos`);
}
console.log("Usuários inseridos com sucesso");
} catch (error) {
console.error("Erro ao cadastrar usuários", error);
}
}
Após salvar, podemos executar o código. Anteriormente, o processo demorou cerca de dois minutos. Vamos verificar quanto tempo levará agora.
A função utilizada é a mesma de antes. Podemos usar o comando npx ts-node-dev src/seed.ts
. Ao executar, a conexão é feita e os dados começam a ser inseridos em uma velocidade satisfatória. No Beekeeper, ao atualizar a tabela, observamos que foi criada corretamente e os dados estão sendo inseridos rapidamente. Com essa abordagem, conseguimos inserir dados de forma mais rápida.
Vamos entender o motivo disso. Ao usar await Promise.all
, lidamos com um array de promessas resolvidas de forma concorrente. Isso não significa paralelismo, mas sim concorrência. Podemos explorar essa diferença em vídeos futuros. Basicamente, o método inicia a resolução de uma promessa e, antes de finalizar, já começa outra em paralelo. Quando todas são concluídas, ele retorna os resultados, evitando a inserção sequencial e tornando o processo mais eficiente.
BulkCreate
para otimizarO tempo de execução foi de 38 segundos, o que representa um ganho significativo em relação aos 2 minutos anteriores. Ao verificar novamente, confirmamos que os 200 mil usuários foram inseridos. Vamos apagar a tabela e explorar uma forma de melhorar essa abordagem. Podemos comentar o await Promise.all
e, como estamos usando o Sequelize, utilizar a função BulkCreate
. Para usar o BulkCreate
, basta chamar await User.bulkCreate
e passar o lote de dados.
await User.bulkCreate(batch);
Após salvar, executamos novamente. A inserção foi concluída em 10 segundos, comparado aos 40 segundos anteriores e aos 2 minutos iniciais. O BulkCreate
insere dados em lotes de mil, enquanto o Promise.all
resolve promessas em paralelo, mas ainda uma de cada vez. O BulkCreate
insere diretamente no banco de dados, resultando em um ganho de performance significativo, cerca de quatro vezes mais rápido que a abordagem anterior.
Essa técnica é recomendada para gerar bases de dados ou realizar migrações. Se o ORM utilizado suporta o BulkCreate
, é uma excelente abordagem. Caso contrário, a abordagem com Promise.all
ainda oferece um ganho de performance em comparação a inserir dados um por um em um loop.
O curso Node.js: gerenciando threads e processos possui 168 minutos de vídeos, em um total de 51 atividades. Gostou? Conheça nossos outros cursos de Java em Programação, ou leia nossos artigos de Programação.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.