Cron jobs, workers em segundo plano ou filas

Use cron para trabalho ligado ao relógio, fila para trabalho disparado por um evento e worker como o processo que consome essa fila. A maioria das aplicações reais combina os três.

Atualizado em 6 min de leitura

Objetivos de aprendizado

Depois de ler este artigo, você vai conseguir:

  • Diferenciar cron jobs, workers e filas e o que cada um resolve
  • Escolher a ferramenta certa para uma tarefa agendada, disparada por evento ou pesada
  • Montar uma fila mínima com um worker que tenta de novo em Node.js
Nesta página

Cedo ou tarde, alguma tarefa fica lenta demais para terminar antes de o usuário receber a resposta: enviar um e-mail, redimensionar uma imagem, chamar uma API de terceiros instável. Outras tarefas nem têm requisição, como uma limpeza de madrugada. Três ferramentas cobrem esse terreno, e muita gente as confunde: cron jobs, workers em segundo plano e filas.

Elas não são rivais. Respondem a perguntas diferentes: quando o trabalho começa, quem o faz e onde ele espera.

quando começaonde esperaquem executaRelógiosem filaCron jobexecuta a tarefaRequisiçãoum eventoGatilhocronFilajobs no RedisWorker 1Worker 2nova tentativa
O cron decide quando, a fila guarda o job até a vez dele e o worker o executa, com novas tentativas.

O que é cada um?#

Um cron job inicia uma tarefa em horários definidos por um agendamento. Nada o dispara além do relógio. Veja o que é um cron job para a sintaxe.

Um worker em segundo plano é um processo de longa duração que trabalha fora do ciclo da requisição. Ele é definido por onde roda, não por como recebe trabalho. Um worker pode consultar um banco de dados periodicamente, escutar uma fila ou rodar um loop.

Uma fila é um buffer de jobs. Um produtor (a sua aplicação web) adiciona um job e segue em frente. Um worker o pega depois. Filas costumam oferecer novas tentativas, atrasos, prioridades e limites de concorrência.

Como eles se comparam lado a lado?#

Cron job Worker em segundo plano Fila
Começa por causa de O relógio O que você programar Um job foi adicionado
Reage a eventos Não Sim, se estiver escutando Sim
Tenta de novo em caso de falha Não, a menos que você programe Só se você programar Normalmente já vem embutido
Sobrevive a um restart no meio da tarefa Não Não Os jobs pendentes sobrevivem, porque ficam guardados
Aguenta picos Mal Limitado a um processo Bem: os jobs esperam a vez
Infraestrutura extra Nenhuma Um processo Um broker (por exemplo, Redis)

As duas últimas linhas são as que mais importam. Uma fila separa a produção do trabalho da execução, então um pico de 1.000 jobs não exige 1.000 processos simultâneos.

Quando usar cron?#

Use cron quando o gatilho é o tempo e a tarefa é autocontida: limpezas noturnas, relatórios diários, renovação de tokens, sincronização de dados a cada hora. Ele não precisa de broker nem de peças extras.

O cron deixa a desejar quando uma execução falha e precisa ser repetida, quando uma execução dura mais que o intervalo ou quando cada execução precisa se dividir em muitas partes. Um agendador dentro da aplicação também dispara uma vez por instância, então, se você rodar duas cópias da aplicação, as duas disparam o job.

Quando usar uma fila e um worker?#

Use uma fila quando algo aconteceu e um trabalho precisa vir em seguida: um usuário se cadastrou (enviar o e-mail de boas-vindas), um arquivo foi enviado (gerar miniaturas), um pagamento foi confirmado (emitir o recibo). A requisição responde rápido, e o worker termina o trabalho.

Escolha uma fila quando precisar de um destes recursos:

  • Novas tentativas com backoff para dependências instáveis.
  • Controle de taxa, por exemplo no máximo 5 chamadas por segundo a uma API.
  • Escalar o trabalho, iniciando mais processos de worker.
  • Nenhum trabalho perdido quando um deploy ou uma falha interrompe o processo, porque os jobs pendentes ficam no broker.

Um job pode rodar mais de uma vez, por exemplo quando um worker cai no meio dele (o BullMQ descreve "pelo menos uma vez" como o seu pior caso). Por isso, escreva jobs idempotentes: rodar o mesmo job duas vezes não pode cobrar um cliente duas vezes.

Como é uma fila mínima em Node.js?#

O BullMQ é uma biblioteca de filas para Node.js construída sobre o Redis. Instale com npm install bullmq. O produtor adiciona jobs:

js
// producer.js (precisa de "type": "module" no package.json)
import { Queue } from "bullmq";

const connection = {
  host: process.env.REDIS_HOST,
  port: Number(process.env.REDIS_PORT),
  password: process.env.REDIS_PASSWORD,
};

export const emailQueue = new Queue("emails", {
  connection,
  defaultJobOptions: {
    attempts: 3,
    backoff: { type: "exponential", delay: 1000 },
  },
});

await emailQueue.add("welcome", { to: "ana@example.com" });

O worker é um processo separado que os consome:

js
// worker.js
import { Worker } from "bullmq";

const connection = {
  host: process.env.REDIS_HOST,
  port: Number(process.env.REDIS_PORT),
  password: process.env.REDIS_PASSWORD,
};

const worker = new Worker(
  "emails",
  async (job) => {
    // lance um erro aqui e o BullMQ tenta o job de novo
    console.log("enviando e-mail de boas-vindas para", job.data.to);
  },
  { connection, concurrency: 5 },
);

worker.on("failed", (job, err) => {
  console.error(`job ${job?.id} falhou:`, err.message);
});

Com attempts: 3 e um backoff exponencial de 1.000 ms, um job que falha é repetido depois de cerca de 1 s e de 2 s antes de ser marcado como falho. concurrency: 5 permite que um worker processe cinco jobs ao mesmo tempo.

Dois detalhes do Redis importam em produção, segundo a documentação do BullMQ: configure maxmemory-policy como noeviction, para que o Redis nunca descarte chaves da fila sem avisar, e mantenha maxRetriesPerRequest como null nas conexões dos workers (o padrão do Worker).

Dá para combinar os três?#

Dá, e a maioria das aplicações maduras faz isso. O cron é um bom gatilho, a fila é um bom buffer e o worker é quem executa. Os job schedulers do BullMQ adicionam jobs a uma fila seguindo um padrão cron:

js
await emailQueue.upsertJobScheduler(
  "daily-digest",
  { pattern: "0 0 8 * * *", tz: "America/New_York" },
  { name: "digest", data: {} },
);

O padrão tem seis campos, com os segundos primeiro, como nos exemplos da documentação do BullMQ (este significa 08:00:00 todos os dias). Agora o agendamento fica no Redis, e não na memória de um único processo, e o trabalho ganha novas tentativas e controle de concorrência como qualquer outro job.

Como escolher?#

  • Uma tarefa curta e inofensiva no relógio: cron, dentro da aplicação ou no sistema.
  • Trabalho causado por um evento, ou trabalho que pode falhar e precisa ser repetido: fila mais worker.
  • Uma tarefa agendada que é pesada ou não pode se perder: gatilho cron alimentando uma fila.
  • Uma tarefa que é só um loop longo, como o consumidor de um stream: um worker sozinho.

Comece pela opção menor. Trocar o cron por uma fila depois é fácil se a lógica do job ficar numa função que não se importa com quem a chama. Para o lado do Redis, veja o que é Redis e como hospedar uma aplicação Node.js.

Como fazer isso na Square Cloud#

Workers e agendadores precisam de processos que fiquem ligados. Na Square Cloud, os containers ficam sempre ligados, sem modo de suspensão, então um worker ou um agendador dentro da aplicação continua rodando. Com o reinício automático ligado (o padrão no deploy pelo painel), a aplicação volta sozinha quando o processo encerra com erro.

Para uma fila, faça o deploy do worker como uma aplicação própria, ao lado da sua aplicação web. A RAM do plano é um pool compartilhado entre as suas aplicações, com mínimo de 256 MB por aplicação de fundo e 512 MB por aplicação web. A hospedagem de Redis está disponível a partir do plano Standard, no mesmo datacenter das aplicações, com conexões TLS e persistência. O painel entrega a URL de conexão e o certificado do banco. Coloque os dados de conexão em variáveis de ambiente. Como o TLS é obrigatório e usa certificado próprio, adicione ao objeto connection dos exemplos acima uma opção tls com a CA baixada (por exemplo tls: { ca: fs.readFileSync("ca.crt") }).

Faça o deploy pelo painel, pela CLI ou pelo GitHub. O arquivo de configuração squarecloud.app é opcional: o painel gera um no deploy, com runtime, memória e arquivo principal. Veja as opções para hospedar APIs para começar.

Hospedagem de APIs

APIs em containers sempre ligados, sem cold start

Saiba mais

Perguntas frequentes

Fila e worker são a mesma coisa?

Não. A fila guarda os jobs que esperam para ser feitos. O worker é o processo que tira jobs dela e os executa. Você precisa dos dois para que a fila sirva para alguma coisa.

Posso usar cron no lugar de uma fila?

Só para trabalho que depende do horário. Se a tarefa começa porque algo aconteceu, como um pagamento ou um upload, a fila reage na hora e tenta de novo quando falha, o que o cron não faz.

Filas precisam de Redis?

Não necessariamente: brokers como RabbitMQ e Amazon SQS funcionam sem Redis. Já bibliotecas como o BullMQ, para Node.js, guardam os jobs no Redis e por isso precisam de um servidor Redis.

Como rodar um job recorrente com uma fila?

O BullMQ tem job schedulers que adicionam um job à fila seguindo um padrão cron ou um intervalo fixo. O agendamento fica no Redis, e qualquer worker pode processar o job.

Próximo artigoWebSocket vs webhook vs SSEWebSocket é um canal bidirecional, SSE um fluxo do servidor para o navegador e webhook um aviso HTTP entre servidores. Compare e escolha com exemplos de código.