Como desenvolvedor da Web, é crucial que seus aplicativos sejam executados o mais rápido possível. Você deve criar aplicativos da web que respondam às solicitações no menor tempo possível.
Uma das muitas tecnologias que podem ajudá-lo é o enfileiramento de tarefas.
Então, o que é enfileiramento de tarefas e como você pode usá-lo para otimizar um aplicativo Node.js?
O que é enfileiramento de tarefas?
O enfileiramento de mensagens é um meio de comunicação assíncrona entre dois aplicativos ou serviços, geralmente chamado de produtor e consumidor. É um conceito bem conhecido empregado em arquiteturas sem servidor e de microsserviço.
O conceito de tarefa ou trabalhofila aproveita o enfileiramento de mensagens para melhorar o desempenho do aplicativo. Ele abstrai as complexidades do gerenciamento de mensagens e permite definir funções para gerenciar trabalhos ou tarefas de forma assíncrona usando uma fila, reduzindo assim a taxa de
uso de memória em algumas partes de um aplicativo.O exemplo mais comum de software de fila de mensagens é o RabbitMQ. As ferramentas de fila de tarefas incluem Celery e Bull. Você também pode configurar o RabbitMQ para funcionar como uma fila de tarefas. Continue lendo para aprender sobre enfileiramento de tarefas em Node.js usando Bull.
O que é BullMQ?
BullMQ (Bull.js) é uma biblioteca Node.js usada para implementar filas em aplicativos Node. Bull é um sistema baseado em Redis (você pode estar mais familiarizado com o Redis como uma ferramenta para armazenamento rápido de dados) e é uma opção rápida e confiável a ser considerada para o enfileiramento de tarefas no Node.js.
Você pode usar o Bull para muitas tarefas, como implementação de trabalhos atrasados, trabalhos agendados, trabalhos repetíveis, filas de prioridade e muito mais.
Então, como você pode usar Bull e Redis para executar tarefas Node.js de forma assíncrona?
Como configurar Bull e Redis para enfileiramento de tarefas no Node.js
Para começar a enfileirar tarefas no Node.js com Bull, você precisa do Node.js e do Redis instalados em sua máquina. Você pode seguir o Guia dos laboratórios Redis para instalar o Redis se você não o tiver instalado.
O primeiro passo para implementar o Bull é adicioná-lo às dependências do seu projeto executando touro de instalação npm ou fio adicionar touro no terminal dentro da pasta do seu projeto. Existem várias maneiras de inicializar uma fila no Bull, conforme mostrado abaixo:
const Fila = exigir('touro');
// maneiras diferentes de inicializar uma fila
// - usando string de URL redis
const emailQueue = novo Fila('Fila de e-mail', 'redis://127.0.0.1:6379');// - com uma conexão redis e um objeto de opções de fila
const videoQueue = novo Fila('Fila de vídeo', 'redis://127.0.0.1:6379', filaOpções);// - sem conexão redis mas com queueOption
const docQueue = novo Fila('Fila de Documentos', filaOpções);
// - sem uma conexão redis ou opções de fila
const QueueClient = novo Fila('Minha Fila');
Todos eles usam configuração mínima para Bull em Node.js. O objeto de opções suporta muitas propriedades e você pode aprender sobre elas no seção de opções de fila da documentação do Bull.
Implementando uma fila de tarefas de e-mail usando BullMQ
Para implementar uma fila para envio de e-mails, você pode definir sua função de produtor que adiciona e-mails à fila de e-mails e uma função de consumidor para lidar com o envio de e-mails.
Em primeiro lugar, você pode inicializar sua fila em uma classe usando uma URL do Redis e algumas opções de fila conforme mostrado abaixo.
// filaHandler.js
const Fila = exigir('touro');// use um módulo manipulador de e-mail real aqui - este é apenas um exemplo
const emailHandler = exigir('./emailHandler.js');// define constantes, URL do Redis e opções de fila
const REDIS_URL = 'redis://127.0.0.1:6379';const filaOpts = {
// opções do limitador de taxa para evitar sobrecarregar a fila
limitador: {
// número máximo de tarefas que a fila pode levar
máximo: 100,// tempo de espera em milissegundos antes de aceitar novos trabalhos após
// atingindo o limite
duração: 10000
},
prefixo: 'E-MAIL-TAREFA', // um prefixo a ser adicionado a todas as chaves da fila
defaultJobOptions: { // opções padrão para tarefas na fila
tentativas: 3, // número padrão de vezes para repetir uma tarefa// para remover uma tarefa da fila após a conclusão
removeOnComplete: verdadeiro
}
};aulaEmailQueue{
construtor() {
esse.fila = novo Fila('Fila de e-mail', REDIS_URL, queueOpts);
}
};
exportarpadrão EmailQueue; // exporta a classe
Agora que você inicializou uma fila, você pode definir sua função de produtor (usando Bull's adicionar() função) como um método do EmailQueue class para adicionar emails à fila de tarefas. O seguinte bloco de código demonstra isso:
// filaHandler.js
aulaEmailQueue{
construtor () {
// ...
}// função produtora para adicionar e-mails à fila
assíncrono addEmailToQueue (emailData) {
// adiciona a tarefa com o nome 'email_notification' à fila
aguardamesse.queue.add('Notificação de Email', emailData);
console.registro('o e-mail foi adicionado à fila...');
}
};
exportarpadrão EmailQueue; // exporta a classe
A função produtora está pronta, e agora você pode definir uma função consumidora (usando o Bull's processo() função) para processar todas as tarefas de e-mail na fila, ou seja, chame a função para enviar um e-mail. Você deve definir essa função de consumidor no construtor da classe.
// filaHandler.js
aulaEmailQueue{
construtor () {
// ...// função do consumidor que recebe o nome atribuído da tarefa e
// uma função de retorno de chamada
esse.fila.processo('Notificação de Email', assíncrono (emailJob, concluído) => {
console.registro('tarefa de notificação de e-mail de processamento');
aguardam emailHandler.sendEmail (emailJob); //envia o e-mail
feito(); // complete a tarefa
})
}
// ...
};
exportarpadrão EmailQueue; // exporta a classe
Um trabalho também pode ter opções para definir seu comportamento na fila ou como a função do consumidor o trata. Você pode saber mais sobre isso no seção de opções de trabalho da documentação da Bull.
O emailJob argumento é um objeto que contém as propriedades da tarefa para a fila processar. Também inclui os principais dados necessários para construir o e-mail. Para facilitar o entendimento, o enviar email() função seria semelhante a este exemplo:
// emailHandler.js
const sendgridMail = exigir('@sendgrid/mail');const apiKey = process.env. SENDGRID_API_KEY
sendgridMail.setApiKey (apiKey); // define as credenciais de segurança do transportador de e-mail
const enviarEmail = assíncrono (emailJob) => {
tentar {
// extrai os dados de e-mail do trabalho
const { nome, email } = emailJob.data;const mensagem = {
de: '[email protected]',
para: 'você@exemplo.com',
assunto: 'Oi! Bem-vindo',
texto: `Olá ${nome}, bem-vindo ao MUO`
};aguardam sendgridMail.sendMail (mensagem); // enviar email
// marca a tarefa como concluída na fila
aguardam emailJob.moveToCompleted('feito', verdadeiro);
console.registro('E-mail enviado com sucesso...');
} pegar (erro) {
// move a tarefa para trabalhos com falha
aguardam emailJob.moveToFailed({ mensagem: 'falha no processamento da tarefa..' });
console.erro (erro); // registra o erro
}
}
exportarpadrão enviar email;
Agora que você tem as funções de produtor e consumidor definidas e prontas para uso, agora você pode chamar sua função de produtor em qualquer lugar em seu aplicativo para adicionar um e-mail à fila para processamento.
Um controlador de exemplo ficaria assim:
// userController.js
const EmailQueue = exigir('../handlers/queueHandler.js')const inscrever-se = assíncrono (req, res) => {
const { nome, e-mail, senha } = req.body;// --
// uma consulta para adicionar o novo usuário ao banco de dados...
// --// adiciona à fila de e-mail
const emailData = { nome, email };
aguardam EmailQueue.addEmailToQueue (emailData);
res.status(200).json({
mensagem: "Inscrição bem-sucedida, por favor, verifique seu e-mail"
})
}
Seu queueHandler.js arquivo agora deve ser o seguinte:
// filaHandler.js
const Fila = exigir('touro');
const emailHandler = exigir('../handlers/emailHandler.js');const REDIS_URL = 'redis://127.0.0.1:6379';
const filaOpts = {
limitador: {
máximo: 100,
duração: 10000
},prefixo: 'E-MAIL-TAREFA',
defaultJobOptions: {
tentativas: 3,
removeOnComplete: verdadeiro
}
};aulaEmailQueue{
construtor() {
esse.fila = novo Fila('Fila de e-mail', REDIS_URL, queueOpts);// consumidor
esse.fila.processo('Notificação de Email', assíncrono (emailJob, concluído) => {
console.registro('tarefa de notificação de e-mail de processamento');
aguardam emailHandler.sendEmail (emailJob);
feito();
})
}// produtor
assíncrono addEmailToQueue (emailData) {
// adiciona a tarefa com o nome 'email_notification' à fila
aguardamesse.queue.add('Notificação de Email', emailData);
console.registro('o e-mail foi adicionado à fila...');
}
};
exportarpadrão EmailQueue;
Ao implementar isso em uma API REST Node.js, você notará uma diminuição no tempo de resposta do endpoint de inscrição e tempos de entrega de e-mail mais rápidos em comparação com a alternativa.
As filas de tarefas também permitiram que você lidasse com erros de inscrição e e-mail de forma independente.
Otimizando aplicativos usando filas de tarefas
As filas de mensagens e tarefas são uma ótima maneira de melhorar o desempenho geral dos aplicativos. Eles também são muito baratos e você pode usá-los em quantas partes de um aplicativo precisar.
Embora este tutorial tenha usado e-mails como um cenário de exemplo para lidar com tarefas que consomem muita memória com filas, há muitos outros casos em que você pode aplicar os mesmos conceitos. Isso inclui operações pesadas de leitura/gravação, renderização de imagens ou documentos de alta qualidade e envio de notificações em massa.