04 - Message Routing (Tópicos, Filtros)
video: https://youtu.be/... no frontmatter da nota.Message Routing (Tópicos, Filtros)
Message routing é o conjunto de mecanismos que decide, dentro do broker, qual mensagem vai para qual destino, sem que o produtor conheça os consumidores. Em vez de espalhar tudo para todos ou criar uma fila por interesse, o desenho de roteamento usa tópicos, chaves de roteamento e filtros para entregar recortes precisos de um fluxo para múltiplos consumidores. O ganho operacional é direto: a decisão de distribuição fica declarada na topologia do broker, configurável e auditável, e não espalhada em ifs no código dos produtores. Em produção, roteamento bem desenhado é o que permite um fluxo de eventos servir cem consumidores com custos e interesses distintos, e roteamento mal desenhado é o que gera fanout desperdiçado, acoplamento oculto e incidentes de mensagem no destino errado. Este arquivo cobre os mecanismos de roteamento dos brokers mais usados e as práticas de taxonomia que sustentam o crescimento do catálogo de tópicos.
Conceito: roteamento como parte do contrato
Roteamento de mensagens é o ato de classificar cada mensagem no envio e filtrar cada assinatura no recebimento. A classificação usa metadados, tipicamente a routing key e os atributos da mensagem, e a filtragem compara esses metadados com as regras declaradas em cada binding ou assinatura.
A consequência arquitetural é a inversão do controle de distribuição. O produtor declara o que a mensagem é; o consumidor declara o que lhe interessa; o broker faz o casamento. Nenhum dos lados conhece a lista do outro, e novas rotas nascem por configuração, sem deploy de código.
Essa fronteira tem nome na prática: o contrato do produtor é o schema mais os metadados de roteamento, e a lista de assinantes é detalhe privado de cada consumidor. Times que misturam esses papéis, com o produtor publicando em filas específicas de consumidores, perdem a principal vantagem do modelo.
O roteamento também é instrumento de observabilidade. Como a decisão fica no broker, ela é configurável, versionável e visível: bindings, políticas e catálogo formam a planta da distribuição, e perguntas como quem consome este evento ou o que acontece com esta mensagem têm resposta consultável. Quando a decisão mora em código disperso, essas perguntas exigem caça a logs e deploys.
O problema que resolve
O problema elementar é a explosão de filas ponto a ponto. Sem roteamento, cada interesse novo exige o produtor publicando em mais uma fila, e o acoplamento de notificação volta pela janela. Com roteamento, o produtor publica uma vez no tópico, e interesses novos entram como bindings ou filtros.
O segundo problema é o fanout desperdiçado. Um tópico de pedidos com cinquenta assinantes, em que apenas um interessa-se por pedidos internacionais, entrega noventa e oito por cento de mensagens descartadas se não houver filtros. Filtros de assinatura movem o descarte do consumidor para o broker, poupando rede, custo de entrega e processamento.
O terceiro problema é a flexibilidade de interesse. Consumidores diferentes querem recortes diferentes do mesmo fluxo: tudo de uma categoria, uma subárvore do domínio, mensagens com um atributo específico. Mecanismos de roteamento expressam esses recortes declarativamente, sem precisar de um tópico para cada combinação possível.
Há ainda o problema de governança sob crescimento. Sem roteamento central, a matriz produtor por consumidor cresce quadraticamente com o número de sistemas, e cada integração exige coordenar dois times e dois deploys. Com roteamento declarado no broker, a matriz deixa de ser ponto a ponto e passa por um hub com contrato, e o custo de cada nova integração cai para o trabalho de apenas um lado.
Exchanges do RabbitMQ: roteamento por binding
O RabbitMQ separa publicação de entrega com o exchange, um roteador sem estado que aplica regras de binding entre o exchange e as filas. O produtor publica no exchange com uma routing key, e o exchange decide quais filas recebem a cópia.
O exchange direct entrega apenas às filas cujo binding tem a routing key exata da mensagem; é o despacho pontual. O exchange fanout ignora a chave e entrega a todos os bindings; é o broadcast puro. O exchange topic compara a routing key com padrões que usam asterisco para uma palavra e cerquilha para zero ou mais palavras; é o roteamento hierárquico, o mais usado em domínios grandes. O exchange headers roteia por atributos do header da mensagem, com quantificadores any e all, útil quando o critério não cabe em uma chave hierárquica.
Além dos quatro tipos, o RabbitMQ oferece o alternate exchange, um destino de segurança para mensagens que não casam com nenhum binding. Configurá-lo converte roteamento falho de perda silenciosa em rota observável, e é a prática mais barata de confiabilidade que uma topologia AMQP pode adotar.
| Exchange | Critério | Exemplo de binding | Uso típico |
|---|---|---|---|
| direct | Igualdade exata da routing key | pagamento.aprovado | Despacho de comandos específicos |
| topic | Padrões com * e # | pedido.*.aprovado | Hierarquia de eventos com recortes |
| fanout | Ignora routing key | qualquer | Broadcast para N filas |
| headers | Atributos do header | tipo=cobranca, regiao=br | Múltiplos critérios não hierárquicos |
# Topologia declarativa no RabbitMQ: o produtor não conhece filas
canal.exchange_declare(exchange="pedidos", exchange_type="topic")
canal.queue_declare(queue="logistica.internacional", durable=True)
canal.queue_bind(
queue="logistica.internacional",
exchange="pedidos",
routing_key="pedido.internacional.*", # recorte por wildcard
)
canal.queue_declare(queue="auditoria.geral", durable=True)
canal.queue_bind(
queue="auditoria.geral",
exchange="pedidos",
routing_key="pedido.#", # tudo da árvore pedido
)
# Publicação única; o exchange resolve os destinos
canal.basic_publish(
exchange="pedidos",
routing_key="pedido.internacional.criado",
body=payload,
)
# entregue para logistica.internacional e auditoria.geral
Filtros de assinatura no SNS e equivalentes
No modelo AWS, o tópico do SNS entrega para assinaturas de qualquer protocolo, e cada assinatura pode carregar uma filter policy, um documento JSON de critérios sobre atributos da mensagem. A política suporta igualdade, prefixo, sufixo, anything-but, intervalos numéricos e combinação lógica com and e or, o que cobre a maioria dos recortes de negócio sem código no consumidor.
O efeito prático é a distribuição seletiva com economia real: uma assinatura de SQS com filtro recebe apenas o subconjunto filtrado, e a fila, o custo de entrega e o processamento acompanham o recorte. Sem filtro, o padrão fanout SNS para SQS entrega tudo, e o consumidor descarta no destino.
Kafka atinge o mesmo objetivo por outra via: filtragem no consumidor ou em componentes intermediários como Kafka Streams e conectores, já que o broker não filtra. A diferença de filosofia importa no desenho: no RabbitMQ e no SNS o filtro é infraestrutura declarativa; no Kafka é código no caminho do consumidor, com o custo de rede correspondente.
| Critério | Filtro no broker | Filtro no consumidor |
|---|---|---|
| Custo de rede | Só o recorte trafega | Tudo trafega até o destino |
| Local da decisão | Declarativa, na topologia | Código no caminho de processamento |
| Latência de mudança | Alteração de configuração | Deploy de código |
| Complexidade expressiva | Limitada aos operadores do broker | Qualquer lógica programável |
| Exemplos | Bindings AMQP, FilterPolicy do SNS | Consumidores Kafka, Streams, Flink |
A escolha prática mistura os dois: filtros grossos no broker reduzem entrega, e refinamentos finos que exigem lógica ficam no consumidor. O erro é inverter, mandando o broker tomar decisões que só o código conhece, ou o consumidor descartar volume que uma política simples já resolveria.
O roteamento por header merece atenção especial nos limites. Critérios com muitos atributos e combinações crescem exponencialmente em bindings e políticas, e cada header extra adicionado a um fluxo popular vira um novo eixo de explosão combinatória. A disciplina é limitar o vocabulário de roteamento a poucos atributos estáveis, tratando qualquer expansão como decisão arquitetural, e não como conveniência de um time.
{
"FilterPolicy": {
"moeda": ["BRL"],
"valor": [{"numeric": [">", 1000]}],
"origem": [{"prefix": "matriz-"}]
}
}
Taxonomia de tópicos e chaves de roteamento
A taxonomia é o desenho do espaço de nomes dos tópicos e das chaves, e é onde a maioria dos sistemas erra. A convenção mais sólida usa hierarquia domínio.entidade.evento, com fato no tempo passado: pedidos.cancelado, pagamentos.aprovado, estoque.reservado. O nome revela de onde vem e o que aconteceu, e wildcards de roteamento caem naturalmente sobre a hierarquia.
Chaves de roteamento são para classificação, não para identidade. A entidade específica, como o id do pedido, vai na chave de partição ou no payload, e não na routing key. Routing keys com identificadores únicos fragmentam os bindings e tornam a topologia inadministrável.
A evolução da taxonomia precisa de regras de compatibilidade: renomear tópico é quebrar contrato, e o caminho seguro é publicar no novo e no antigo durante a transição, com consumidores migrando em ordem. Catálogo de tópicos com dono, schema e descrição transforma a taxonomia em infraestrutura navegável, e não em conhecimento tribal.
O mesmo raciocínio vale para atributos usados em filtros. Um dicionário mínimo, com nome, tipo, domínio de valores e semântica, evita o zoo de variantes como moeda, currency e currency_code no mesmo fluxo. Sem padronização, cada nova filter policy vira investigação arqueológica dos payloads reais em produção.
Erros comuns
- Routing key com identificador único da entidade: cada binding vira caso único, e a topologia cresce linearmente com o volume de dados.
- Filtros excessivamente amplos: políticas com qualquer valor aceito entrega o fluxo inteiro, e o filtro dá falsa sensação de recorte sem economia nenhuma.
- Publicar em fila de consumidor específico: some o roteamento, volta o acoplamento, e a troca de consumidor vira mudança no produtor.
- Hierarquia profunda demais: chaves com seis níveis tornam os padrões ilegíveis e os bindings frágeis a qualquer renomeação.
- Wildcard cerquilha em produção sem limite: um binding com pedido.# acumula todo o fluxo, e uma fila lenta a partir dele segura o volume todo.
- Headers como zona livre: cada time inventa atributos próprios, e políticas de filtro quebram na primeira divergência de nomes ou de caixa.
Boas práticas
- Padronize a hierarquia domínio.entidade.evento em toda a organização, com registro central e revisão de novos tópicos, como se faz com endpoints de API.
- Reserve a routing key para classificação estável e coloque identidade no payload ou na chave de partição, mantendo a topologia pequena.
- Prefira filtros no broker quando disponíveis, medindo a redução de entrega antes e depois, para justificar a política com dados.
- Monitore mensagens não roteadas, como o retorno unroutable do RabbitMQ, porque mensagem publicada que não alcança nenhuma fila é perda silenciosa.
- Teste a topologia em CI com publicações de amostra e asserções sobre filas de destino, transformando o roteamento em parte verificada do contrato.
- Documente o dicionário de atributos usados em filtros, pois atributos improvisados sem padronização quebram políticas de assinatura silenciosamente.
- Adote alternate exchange ou rota de fallback em toda topologia, garantindo que mensagem não roteada seja capturada, alertada e auditável.
- Revise bindings e filter policies periodicamente, removendo rotas de assinantes desativados, porque topologia morta custa entrega e esconde acoplamento vivo.