Pub/Sub com Redis

1. Fundamentos do Pub/Sub no Redis

O modelo publisher/subscriber (pub/sub) no Redis implementa um padrão de comunicação assíncrona onde produtores de mensagens (publishers) enviam dados para canais sem conhecimento direto dos consumidores (subscribers). Diferentemente de filas tradicionais como Redis Lists, onde as mensagens são armazenadas e removidas após o consumo, o pub/sub opera no modelo fire-and-forget: mensagens são entregues apenas para subscribers ativos no momento da publicação.

Canais no Redis são identificados por strings arbitrárias e não requerem criação explícita. Ao publicar em um canal inexistente, o Redis simplesmente descarta a mensagem. Padrões de nomenclatura comuns incluem namespaces separados por dois pontos, como notificacoes:usuarios ou eventos:sistema:erros.

Os comandos fundamentais são:

PUBLISH canal "mensagem"
SUBSCRIBE canal1 canal2
UNSUBSCRIBE canal1

O comando SUBSCRIBE coloca a conexão em modo de escuta bloqueante, processando mensagens em tempo real até que o cliente se desconecte ou execute UNSUBSCRIBE.

2. Mecanismo de Mensagens e Entrega

A entrega no Redis Pub/Sub é estritamente fire-and-forget. Quando um publisher envia uma mensagem via PUBLISH, o Redis a distribui imediatamente para todos os subscribers ativos naquele canal. Não há confirmação de recebimento, retenção em buffer ou reenvio em caso de falha.

A ordem de entrega é garantida por conexão: mensagens publicadas em um mesmo canal chegam na mesma ordem para todos os subscribers. Contudo, subscribers em conexões diferentes podem processar mensagens em velocidades distintas devido à concorrência natural do sistema.

A principal limitação é a perda de mensagens quando não há subscribers ativos. Se um publisher envia dados para um canal vazio, a mensagem é descartada sem qualquer notificação. Isso torna o pub/sub inadequado para comunicação confiável onde cada mensagem deve ser processada exatamente uma vez.

# Exemplo de perda de mensagem
PUBLISH alertas "servidor_caido"  # Nenhum subscriber ativo -> mensagem perdida

3. Padrões de Subscrição com PSUBSCRIBE

O comando PSUBSCRIBE permite inscrição em múltiplos canais usando padrões glob-style, similares a expressões regulares simplificadas:

  • * corresponde a qualquer sequência de caracteres
  • ? corresponde a um único caractere
  • [seq] corresponde a qualquer caractere dentro do conjunto
PSUBSCRIBE notificacoes:*
PSUBSCRIBE eventos.sistema.?
PSUBSCRIBE logs:[erro,aviso]

Canais hierárquicos como notificacoes:email:usuario123 ou eventos:sistema:critico:banco permitem organizar mensagens por domínio e gravidade. Um subscriber com PSUBSCRIBE notificacoes:* receberia todas as mensagens publicadas em qualquer canal iniciado por notificacoes:.

A diferença prática entre SUBSCRIBE e PSUBSCRIBE está no gerenciamento: SUBSCRIBE exige inscrição explícita em cada canal, enquanto PSUBSCRIBE permite capturar famílias inteiras de canais com um único comando. Múltiplos patterns podem ser combinados, e subscribers podem usar ambos os comandos simultaneamente na mesma conexão.

4. Integração com SQL e Banco de Dados Relacional

Uma aplicação poderosa do Redis Pub/Sub é notificar mudanças no banco de dados relacional. Triggers SQL podem invocar funções que publicam eventos no Redis, criando um pipeline de notificações em tempo real.

Exemplo com PostgreSQL e PL/pgSQL:

CREATE OR REPLACE FUNCTION publicar_alteracao_cliente()
RETURNS trigger AS $$
BEGIN
  PERFORM pg_notify('redis_channel', 
    json_build_object(
      'tabela', 'clientes',
      'operacao', TG_OP,
      'id', NEW.id
    )::text);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_cliente_alteracao
AFTER INSERT OR UPDATE OR DELETE ON clientes
FOR EACH ROW EXECUTE FUNCTION publicar_alteracao_cliente();

Neste cenário, um processo externo (como um worker Node.js) escuta notificações PostgreSQL via LISTEN/NOTIFY e as repassa para Redis Pub/Sub, permitindo que múltiplos serviços sejam notificados sobre alterações no banco SQL.

5. Casos de Uso Práticos para Aplicações com SQL

Atualização de dashboards em tempo real: Após inserções em uma tabela de vendas, um publisher Redis envia os novos dados para subscribers que atualizam gráficos e métricas sem polling no banco.

Distribuição entre microsserviços: Serviços que acessam o mesmo banco SQL podem usar Pub/Sub para coordenar cache invalidation. Quando um serviço atualiza um registro, publica no canal cache:invalidar:clientes e todos os subscribers invalidam suas cópias locais.

Exemplo prático com Python (publisher) e Node.js (subscriber consumindo SQL):

# publisher.py
import redis
import psycopg2

conn = psycopg2.connect("dbname=loja")
r = redis.Redis()

cur = conn.cursor()
cur.execute("SELECT id, nome FROM clientes WHERE criado_em > now() - interval '1 minute'")
for row in cur.fetchall():
    r.publish('novos_clientes', f'{{"id": {row[0]}, "nome": "{row[1]}"}}')
// subscriber.js
const redis = require('redis');
const { Client } = require('pg');

const subscriber = redis.createClient();
const pgClient = new Client({ database: 'loja' });

subscriber.subscribe('novos_clientes', (mensagem) => {
  const dados = JSON.parse(mensagem);
  pgClient.query(
    'INSERT INTO dashboard_temp (cliente_id, nome, recebido_em) VALUES ($1, $2, NOW())',
    [dados.id, dados.nome]
  );
});

6. Limitações e Boas Práticas

O Redis é single-threaded para processamento de comandos, o que significa que subscribers bloqueantes consomem ciclos de CPU. Muitos subscribers lentos podem impactar todo o servidor. A recomendação é manter subscribers leves e processar mensagens rapidamente.

Para tratamento de falhas, implemente reconexão automática com resubscrição aos canais anteriores:

import redis
import time

def conectar_com_reconexao():
    r = redis.Redis()
    while True:
        try:
            pubsub = r.pubsub()
            pubsub.subscribe('canais_importantes')
            for mensagem in pubsub.listen():
                processar(mensagem)
        except redis.ConnectionError:
            time.sleep(5)

Comparado ao Redis Streams, o Pub/Sub é mais leve e rápido para notificações em tempo real onde perda ocasional é aceitável. Streams oferecem persistência, confirmação de consumo e replay de mensagens, sendo mais adequados para filas de tarefas e processamento confiável com SQL.

7. Monitoramento e Depuração

O Redis fornece comandos específicos para inspecionar o estado do Pub/Sub:

PUBSUB CHANNELS                    # Lista todos os canais ativos
PUBSUB CHANNELS notificacoes:*     # Filtra por padrão
PUBSUB NUMSUB canal1 canal2        # Número de subscribers por canal
PUBSUB NUMPAT                      # Número de padrões PSUBSCRIBE ativos

Para identificar canais órfãos (com publishers mas sem subscribers), monitore a diferença entre PUBLISH e mensagens recebidas. Ferramentas como RedisInsight ou scripts com INFO commandstats ajudam a avaliar latência e throughput.

Métricas importantes incluem: taxa de publicações por segundo, número médio de subscribers por canal e tempo de processamento de mensagens. Logs de mensagens descartadas indicam necessidade de migração para Redis Streams em cenários críticos.


Referências