Filas de mensagem: por que desacoplar processamento pesado da requisição
Entenda o problema de uma requisição HTTP travada esperando um processamento demorado, como uma fila de mensagem resolve isso de forma assíncrona, e um exemplo prático de produtor e consumidor com RabbitMQ.
Já falei aqui sobre webhook e sobre feature flag, dois jeitos de desacoplar partes de um sistema. Fila de mensagem resolve um desacoplamento parecido, mas voltado pra um problema específico: o que fazer quando uma requisição precisa disparar um processamento que demora demais pra terminar dentro do tempo normal de resposta de uma API.
O problema da requisição travada
Imagina um endpoint que, ao ser chamado, precisa gerar um relatório em PDF com milhares de linhas, ou mandar e-mail pra uma lista de dez mil usuários. Se esse processamento inteiro roda dentro da própria requisição HTTP, o usuário fica esperando, a conexão fica aberta o tempo todo, e se o processamento demorar mais que o timeout configurado (no navegador, no load balancer, ou no próprio servidor), a requisição simplesmente falha, mesmo que o processamento continuasse rodando por trás.
// ruim: a requisição só responde depois do trabalho pesado terminar
app.post("/relatorios", async (req, res) => {
const pdf = await gerarRelatorioGigante(req.body);
res.json({ pdf });
});Como a fila resolve isso
Uma fila de mensagem separa dois papéis: quem pede o trabalho (o produtor) e quem executa o trabalho (o consumidor), e no meio dos dois fica a fila, seja RabbitMQ, SQS, ou outra ferramenta parecida, guardando as mensagens até que alguém esteja pronto pra processar cada uma.
Em vez do endpoint fazer o trabalho pesado direto, ele só publica uma mensagem na fila e responde na hora:
// produtor: só publica a mensagem, não faz o trabalho pesado
app.post("/relatorios", async (req, res) => {
await fila.publicar("gerar_relatorio", { usuarioId: req.body.usuarioId });
res.json({ status: "processando" });
});Um processo separado, rodando de forma independente do servidor da API, fica escutando essa fila e processa cada mensagem no próprio ritmo:
// consumidor: roda separado, processa uma mensagem por vez
fila.consumir("gerar_relatorio", async (mensagem) => {
const pdf = await gerarRelatorioGigante(mensagem.usuarioId);
await salvarPdf(mensagem.usuarioId, pdf);
await notificarUsuario(mensagem.usuarioId);
});A resposta da API volta imediatamente, avisando que o processamento começou, e o usuário é notificado depois (por e-mail, por WebSocket, que já expliquei aqui, ou checando um status numa tela) quando o relatório realmente fica pronto.
O ganho além da resposta rápida
Separar produtor e consumidor traz outro benefício que passa despercebido no começo: os dois lados podem escalar de forma independente. Se a fila acumula mensagem mais rápido do que consegue processar, basta subir mais instâncias do consumidor, sem precisar tocar no servidor da API. E se o consumidor cair por qualquer motivo, a mensagem continua guardada na fila, esperando alguém processar, em vez de simplesmente se perder como aconteceria se o processamento estivesse preso dentro da própria requisição HTTP.
Reprocessamento e fila de erro
Quando um consumidor falha ao processar uma mensagem, o comportamento padrão da maioria das filas é devolver ela pra fila, pra tentar de novo depois. Isso é útil pra falha temporária (um serviço externo fora do ar por um instante), mas perigoso sem limite: uma mensagem com problema permanente (um dado inválido que sempre quebra o processamento) ficaria tentando pra sempre, num loop infinito.
A solução padrão é a dead letter queue, uma fila separada pra onde a mensagem vai depois de um número máximo de tentativas falhas:
fila.consumir("gerar_relatorio", async (mensagem) => {
try {
await gerarRelatorioGigante(mensagem.usuarioId);
} catch (erro) {
if (mensagem.tentativas >= 3) {
await fila.publicar("relatorios_com_erro", mensagem);
} else {
throw erro; // devolve pra fila original tentar de novo
}
}
});Isso evita que uma mensagem problemática trave o processamento das outras, e ainda mantém um registro claro do que falhou de verdade, pra alguém investigar depois, em vez de simplesmente sumir sem deixar rastro.
Quando vale a pena
Fila de mensagem não é pra todo tipo de operação. Uma requisição que já responde rápido, tipo buscar um usuário no banco, não ganha nada com essa complexidade extra. O ganho aparece em operações que demoram mais do que um tempo razoável de resposta HTTP, que podem ser processadas depois sem prejudicar a experiência de quem fez a requisição, ou que se beneficiam de escalar o processamento de forma independente da API.
Fechando
O ponto central é separar "receber o pedido" de "executar o trabalho", permitindo que a API responda rápido mesmo quando o trabalho de verdade demora. A fila é o que garante que esse pedido não se perde entre um lado e o outro, mesmo que o consumidor esteja ocupado, fora do ar por um instante, ou processando numa velocidade diferente da que as mensagens chegam.
Leia também
- Edge functions: o que muda rodar código mais perto do usuário18 de setembro de 2026 · 4 min
- Versionamento de API: como mudar um contrato sem quebrar quem já usa17 de setembro de 2026 · 4 min
- GraphQL vs REST: quando cada um faz sentido15 de setembro de 2026 · 4 min
- Migrations de banco de dados: como versionar o schema sem quebrar produção14 de setembro de 2026 · 4 min