NodeJS: manipulando rotinas e processos com PM2 e Cron Jobs.

Mateus Cardoso Santos
6 min readFeb 1, 2022

--

Recentemente tive a necessidade de implementar uma API que, além das rotas comumente utilizadas, realizasse rotinas das quais cumprissem certas tarefas. A intenção é observar quais possíveis problemas podem surgir e como soluciona-los.

As vezes temos situações que o pedido pra uma certa atividade demora uma quantidade X de tempo acima do interessante para uma rota, ou precisamos processar em paralelo N’s requisições pra ’quela tarefa, ou ainda agendar tarefas específicas, e para isso vamos criar uma rotina. Assumindo esse contexto, temos alguns objetivos:

  • Armazenar, se necessário, as informações necessárias pra nossa rotina
  • Criar uma rotina onde execute nossa tarefa de acordo com nossa demanda
  • Configurar o PM2 para usar a aplicação em cluster mode
  • Assumindo que vamos usar a aplicação em n’s processadores, evitar que n’s processos tentem executar a rotina ao mesmo tempo

Configuração inicial

Para dar foco ao nosso objetivo, vamos criar uma API básica, onde as informações são armazenadas num array mesmo. Para o que pretendemos fazer, basta, já que queremos entender como trabalhar com rotinas e processos dentro do nosso backend.

Caso queira ver essa configuração inicial, basta acessar esse link: https://github.com/mugarate12/processes_and_routines/tree/7192fa72ffa18f6a35f71c8a3b87813e01b43965

Aqui temos duas rotas simples, a função delas é “persistir” a informação que posteriormente usaremos paras as nossas rotinas.

Criando Rotina usando Cron Job

Cron Job é uma ferramenta de agendamento que permite configurar tarefas para serem executadas em momentos pré determinados. Assim, podemos tanto definir quando vamos executar alguma ação, ou o quanto deve repetir.

Para isso, vamos precisar de duas bibliotecas, uma delas para o nosso caso já que usamos Typescript. São elas:

npm i -S cron

npm i -S -D @types/cron

Elas vão possibilitar temos acesso essa ferramenta excelente para configurar suas rotinas e tarefas com precisão ou ainda com a repetição que desejar. Sério, é bem poderoso poder fazer isso. Dessa forma, criaremos um uma configuração mínima para servir ao nosso interesse.

O que vamos fazer é simples: criaremos uma função que envie uma mensagem de olá a cada um de nossos usuários contendo a informação que gravamos no campo content e, além disso, vamos mandar tais comprimentos a cada minuto.

Importando o pacote de funcionalidades que a biblioteca “cron” nos dá, tempos dois passos pra criar uma rotina: definir quando irá repetir, e, o que irá ser executado que será nossa função.

A melhor parte está nessa string que informamos quando irá repetir, é isso que nos da o poder que Cron Jobs nos disponibiliza. Com ela, podemos definir n’s possibilidades que, por exemplo:

“0 * * * *”: uma vez por
“0 8 * * *”: todos os dias ás 8h da manhã
“*/15 * * * *”: a cada 15 minutos

Sendo que pra rotinas que irão repetir a cada x tempo, como esta de 15 minutos, ele garante que a sucessão será sempre no minuto 15 daquela hora, no minuto 30, no minuto 45 e finalmente, no minuto 0. Ou seja, não precisamos nos preocupar quando vamos rodar o backend.

Poderíamos, por exemplo, configurar uma rotina onde toda manhã enviasse emails de promoções para nossos clientes, existem muitas possibilidades, basta explorá-las!

Configurando o PM2

PM2 ou Process Manager 2 é um gerenciador de processos avançado e automatizado para aplicações NodeJs. Para este artigo, não vou entrar nos méritos de como instalar, quais comandos ou ainda quais benefícios ou não tem em usá-lo, aqui vamos focar em como fazer uso dele para tornar nossas demandas mais eficientes, entretanto, no README deste repositório há como instala-lo.

Partindo do pressuposto que temos o PM2 devidamente instalado, podemos configura-lo a partir de um json que aqui vou nomear como pm2.json:

Aqui temos algumas propriedades interessantes para discutir, mas antes de tudo, essa ferramenta permite que executemos nossa aplicação em cluster mode sem a necessidade de alterar qualquer linha de código. Cluster mode permite executar nosso app em todas as CPUs disponíveis e usa-las sem qualquer inatividade, dando mais poder para responder ao tráfego, especialmente bom para aplicações que lidam com requisições HTTP.

Aqui vamos separar nossos processos em duas partes na configuração do json, sendo elas, um processo principal que será essencial mais a frente, e quantos processos forem possíveis dada a disponibilidades de CPUs da máquina. Dado isso, vamos observar a propriedade instances que no nosso processo principal nomeado com a propriedade name como “primary”, definimos que devemos ter uma instância do mesmo, e nos demais processos nomeados como “replica”, passando o valor “-1” para a propriedade instances, definimos que o pm2 deve abrir quantos processos forem possíveis. Essa separação pode fazer sentido, e logo mais veremos como nos ajudará na manipulação das nossas rotinas.

Fazendo um rápido overview, temos a propriedade script que referenciamos o arquivo base da nossa aplicação, o instance_var é interessante caso queiramos usar o node app instance, já que as vezes há problemas com a solução padrão do node, e propriedades como env_production e env_development para setar variáveis de ambiente a partir das configurações do PM2.

Vale ressaltar que ainda existe um mar de configurações possíveis, sendo elas usadas de acordo com sua necessidade. Por exemplo, se por algum motivo você precisa que um processo ou mais sejam reiniciados a cada x tempo, ou definir um intervalo pra isso, você pode usar a propriedade cron_restart para usar aquela mesma strutura de string que conversamos anteriormente para definir quando será executada essa tarefa, e o a ferramenta fará pra você.

Após tudo isso, basta executarmos nossa aplicação usando nosso json que pode ser configurado no nosso package.json, basta adicionar o comando:

pm2 start pm2.json

Evitando que n’s processos tentem executar nossas rotinas

Até aqui entendemos como criar rotinas, como usar o PM2 para usar o máximo de processos o possível para aumentar a escalabilidade, todavia temos um problema: caso venhamos a executar nosso projeto, todos os processos tentaram executar nossas rotinas ao mesmo tempo! Isso certamente não é o que queremos e pode causar sérios problemas de performance ou até mesmo de business, imagine que você enviaria a todos os clientes 4 emails iguais da mesma promoção no mesmo horário, isso com certeza é indesejado.

Graças as configurações que fizemos, torna-se prático contornar esse problema e agora sim fará total sentido ter separado nossos processos em um processo principal e outros auxiliares. Podemos recuperar o nome do processo que está executando no código de maneira totalmente prática, e com isso, podemos diferenciar se aquele processo é o principal ou não, podendo assim definir que somente um será responsável por executar nossas rotinas. Excelente, não?

Conseguimos recuperar o nome do nosso processo a partir da variável de ambiente name que o PM2 cria, uma observação é que no ambiente de desenvolvimento caso executemos ele com nodemon, eu seto como “primary” porque nele executaremos um único processo e eu quero que neste, ele execute nossas rotinas.

A partir do nome do nosso processo, podemos identificar se ele tem o que esperamos, e já que configuramos recentemente que nosso processo principal iria se chamar “primary”, procuramos esse identificador no nome do processo e, caso o tenha, faremos todo o processo que já vimos sobre as nossas rotinas. Simples, e agora faz todo sentido tanto a configuração que fizemos quanto a separação dos processos pro nomenclatura.

Considerações finais

Vale ressaltar que a estrutura do projeto e os casos de uso mostrados servem como amostragem das possibilidades que essas ferramentas tem, a robustez e necessidades variam de projeto à projeto.

Nossos ganhos ficam principalmente na praticidade, controle dos processos e rotinas, além do poder pra configurarmos de acordo com as demandas que podemos ter ao longo da vida útil do nosso app.

Caso queira ver o repositório de base deste artigo, segue o link: https://github.com/mugarate12/processes_and_routines

Espero ter contribuído e te ajudado de alguma forma, abraços e bora codar!

--

--

Mateus Cardoso Santos
Mateus Cardoso Santos

Written by Mateus Cardoso Santos

Olá! Meu nome é Mateus, sou um desenvolvedor que gosta de testar, encarar novas abordagens e falar sobre sempre que possivel.

No responses yet