Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Redis Pub/Sub no Node.js: eventos em tempo real

Atualizado em: 27 de outubro de 2026

Rack de servidores processando fluxos de dados no Node.js

Redis Pub/Sub no Node.js permite distribuir mensagens em tempo real entre processos por meio de canais. Um produtor publica um evento e todos os clientes inscritos naquele canal recebem a mensagem enquanto estiverem conectados.

O modelo é simples e rápido, mas possui uma característica decisiva: mensagens não são armazenadas para consumidores desconectados. Por isso, Pub/Sub funciona bem para notificações efêmeras, invalidação de cache, atualização de presença e sinais internos, mas não substitui uma fila durável.

Neste guia, você aprenderá a usar Pub/Sub com o cliente oficial do Redis, separar conexões, criar canais seguros, tratar reconexões, evitar perda silenciosa e decidir quando usar Redis Streams.

Como o Pub/Sub funciona

O Redis mantém canais lógicos. Clientes podem publicar sem conhecer os assinantes:

await publisher.publish('orders.created', JSON.stringify({
  orderId: 'o-123',
  createdAt: new Date().toISOString()
}));

Um assinante registra uma função para receber novas mensagens:

await subscriber.subscribe('orders.created', message => {
  const event = JSON.parse(message);
  console.log(event.orderId);
});

A entrega ocorre apenas para clientes conectados naquele instante. Não existe confirmação, retry automático ou histórico.

Instalando e conectando

npm install redis
import { createClient } from 'redis';

const publisher = createClient({
  url: process.env.REDIS_URL
});

publisher.on('error', error => {
  console.error('Redis publisher error', error);
});

await publisher.connect();

Valide a variável de ambiente antes de iniciar e use TLS quando o Redis estiver fora de uma rede confiável. Consulte também Redis com Node.js: Cache e Filas.

Use uma conexão dedicada para inscrição

Uma conexão em modo subscriber fica dedicada a comandos de Pub/Sub. Duplique o cliente:

const subscriber = publisher.duplicate();

subscriber.on('error', error => {
  console.error('Redis subscriber error', error);
});

await subscriber.connect();
await subscriber.subscribe('orders.created', handleOrderCreated);

Não reutilize a mesma conexão para cache, locks ou comandos comuns. Separar conexões também facilita métricas e shutdown.

Envelope de evento

Evite publicar objetos sem versão. Use um envelope estável:

{
  "id": "evt-01J...",
  "type": "orders.created.v1",
  "occurredAt": "2026-07-26T12:00:00.000Z",
  "source": "orders-api",
  "tenantId": "t-7",
  "data": {
    "orderId": "o-123"
  }
}

O campo id ajuda a rastrear duplicações causadas por reconexão ou lógica do produtor. A versão no tipo permite evoluir o contrato sem quebrar assinantes antigos.

Validação do payload

Mesmo em rede interna, trate a mensagem como entrada externa:

function parseEvent(raw) {
  let value;

  try {
    value = JSON.parse(raw);
  } catch {
    throw new Error('Mensagem Pub/Sub não contém JSON válido');
  }

  if (value?.type !== 'orders.created.v1') {
    throw new Error('Tipo de evento inesperado');
  }

  if (typeof value.data?.orderId !== 'string') {
    throw new Error('orderId inválido');
  }

  return value;
}

Uma biblioteca de schema pode centralizar validação. Não execute comandos, URLs ou nomes de arquivo recebidos sem regras adicionais.

Canais e namespaces

Use nomes previsíveis:

production.orders.created.v1
production.cache.products.invalidated.v1

Inclua ambiente para evitar que desenvolvimento consuma eventos de produção. Em multi-tenancy, normalmente é melhor manter o tenant no payload, evitando milhares de canais e assinaturas difíceis de administrar.

Pattern subscriptions

await subscriber.pSubscribe('production.orders.*', (message, channel) => {
  console.log({ channel, message });
});

Assinaturas por padrão são convenientes, mas podem receber volume maior que o esperado. Monitore canais correspondidos e limite o trabalho feito pelo callback.

Não bloqueie o callback

Processamento pesado dentro do assinante atrasa mensagens seguintes. Encaminhe trabalho para uma fila interna limitada:

const pending = [];
const maxPending = 1000;

function enqueue(message) {
  if (pending.length >= maxPending) {
    throw new Error('Fila local cheia');
  }
  pending.push(message);
}

Para tarefas que não podem ser perdidas, publique diretamente em uma fila durável em vez de depender de memória local.

Reconexão e perda de mensagens

Quando a conexão cai, mensagens publicadas durante a interrupção não são recuperadas. A aplicação deve decidir se isso é aceitável. Para invalidação de cache, uma expiração curta pode funcionar como defesa. Para pagamentos ou pedidos, não é aceitável.

Depois da reconexão, confirme se as inscrições foram restauradas pelo cliente e exponha uma métrica de estado:

let subscribed = false;

await subscriber.subscribe(channel, message => {
  subscribed = true;
  handle(message);
});

Use readiness separada caso o serviço dependa obrigatoriamente da inscrição.

Pub/Sub para invalidação de cache

Um processo altera um produto e publica:

await publisher.publish(
  'cache.products.invalidated.v1',
  JSON.stringify({ productId })
);

Outras instâncias removem a chave local:

await subscriber.subscribe(channel, raw => {
  const { productId } = JSON.parse(raw);
  localCache.delete(`product:${productId}`);
});

A estratégia reduz inconsistência entre instâncias, mas deve ser combinada com TTL. Se um assinante estiver offline, o valor antigo desaparecerá quando expirar.

Pub/Sub com WebSocket

Em aplicações com múltiplas instâncias, Pub/Sub pode distribuir eventos para todos os servidores WebSocket. Cada instância recebe a mensagem e envia somente aos clientes locais interessados.

Consulte WebSocket com Node.js. Aplique autenticação e autorização antes de entregar dados; receber um evento interno não significa que todo socket pode vê-lo.

Presença e eventos efêmeros

Indicadores como “usuário está digitando” são bons candidatos, pois perder uma atualização isolada não compromete o estado permanente. Ainda assim, limite frequência e tamanho das mensagens.

Pub/Sub não é fila

Use uma solução durável quando precisar de:

  • armazenar mensagens;
  • confirmar processamento;
  • repetir falhas;
  • reprocessar histórico;
  • distribuir cada mensagem para apenas um consumidor;
  • controlar backlog.

Redis Streams, RabbitMQ e Kafka atendem cenários diferentes. Pub/Sub envia uma cópia a cada assinante conectado.

Controle de tamanho

Não envie arquivos ou payloads grandes. Publique identificadores e deixe o consumidor buscar o dado autorizado:

{
  "type": "reports.ready.v1",
  "data": { "reportId": "r-10" }
}

Defina um limite de bytes antes de fazer parse. Payloads grandes aumentam latência, memória e pressão de rede.

Segurança

  • use ACLs do Redis;
  • separe credenciais por aplicação;
  • ative TLS fora da rede privada;
  • não exponha o Redis diretamente à internet;
  • não inclua segredos no payload;
  • redija mensagens em logs;
  • valide tenant e autorização no consumidor.

O nome do canal não é um mecanismo de autorização.

Observabilidade

Registre e monitore:

  • estado das conexões;
  • reconexões;
  • mensagens publicadas e recebidas;
  • erros de parsing;
  • tempo do callback;
  • fila local;
  • mensagens descartadas;
  • versões desconhecidas.

Inclua o ID do evento em logs e traces. Veja Distributed Tracing no Node.js.

Shutdown gracioso

async function shutdown() {
  await subscriber.unsubscribe();
  await subscriber.quit();
  await publisher.quit();
}

process.once('SIGTERM', shutdown);
process.once('SIGINT', shutdown);

Primeiro pare novas publicações, depois aguarde callbacks ativos e feche as conexões.

Testes

Teste com uma instância real do Redis:

  • publicação e recebimento;
  • dois assinantes no mesmo canal;
  • payload inválido;
  • mensagem acima do limite;
  • reconexão;
  • assinatura por padrão;
  • shutdown durante processamento;
  • isolamento entre ambientes.

Não baseie o teste apenas em sleeps; use Promises que resolvem quando a mensagem chega, com timeout explícito.

Erros comuns

  • usar a conexão subscriber para comandos comuns;
  • esperar replay de mensagens;
  • processar tarefas críticas sem persistência;
  • não versionar eventos;
  • publicar dados sensíveis;
  • não limitar payload;
  • bloquear o callback;
  • não aplicar TTL ao cache invalidado por Pub/Sub.

Conclusão

Redis Pub/Sub no Node.js é uma ferramenta eficiente para sinais efêmeros e comunicação em tempo real entre instâncias. Sua simplicidade vem da ausência de persistência e confirmação.

Use conexões dedicadas, contratos versionados, payloads pequenos e observabilidade. Quando perder uma mensagem não for aceitável, escolha Redis Streams ou outro broker durável.

Consulte a documentação oficial de Redis Pub/Sub e a documentação do cliente node-redis.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita