Learn RabbitMQ - Advanced Routing - Topics & Patterns
Episode 7 of 33

Learn RabbitMQ - Advanced Routing - Topics & Patterns

Topic exchanges open up pattern-based routing with wildcards. In this episode you master the star and hash rules, design topic hierarchies for routing logs based on severity and module, and get to know headers exchanges with the x-match all and any rules.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Direct exchanges require an exact key match, and fanout can't filter at all. Between the two there is the topic exchange — the most flexible exchange, matching routing keys against wildcard patterns. With topics, a single exchange can serve multi-criteria routing without needing to create a new exchange.

Topic wildcards work like dotted naming patterns: orders.new.europe, log.warning.auth, sensor.suhu.dapur. Producers supply a dotted routing key, and binding keys contain patterns with two special symbols: * for exactly one word, and # for zero or many words. Their combination produces a very expressive routing system.

This episode also introduces the headers exchange, an exchange that ignores the routing key and matches based on key-value pairs in the message headers. After this episode, you will be able to design clean, maintainable routing topologies for large-scale systems.

Topic Exchange Deep Dive

The Star and Hash Wildcard Rules

Three components determine topic matching:

  • * (star) matches exactly one word — the word must not contain a dot.
  • # (hash) matches zero or more words.
  • Ordinary words like orders match themselves literally.
Topic pattern examples
"log.*.error"   → matches: log.api.error, log.worker.error
"log.#"         → matches: log, log.api, log.api.error
"*.new.*"       → matches: orders.new.eu, payments.new.us

Rules to remember: * cannot cross a dot, while # can. The binding log.# accepts every message starting with log., no matter how deep the topic is.

Building Routing with a Topic Exchange

Let's practice. We create a topic exchange for routing logs with the pattern log.<severity>.<module>:

Declare a topic exchange and binding
rabbitmqadmin declare exchange name=log_topic type=topic
rabbitmqadmin declare queue name=q_error durable=true
rabbitmqadmin declare queue name=q_api durable=true
rabbitmqadmin declare binding source=log_topic destination=q_error routing_key=log.error.*
rabbitmqadmin declare binding source=log_topic destination=q_api routing_key=log.*.api

The binding log.error.* sends every log with the error severity to q_error, while log.*.api sends every log from the api module to q_api. Messages matching both are duplicated.

Routing Patterns

Multi-Criteria Routing and Topic Hierarchies

The power of a topic exchange shows when you combine patterns on a single queue. A queue can receive messages from several patterns at once:

PythonPublish with a hierarchical routing key
channel.basic_publish(
    exchange="log_topic",
    routing_key="log.warning.auth",
    body=b'{"pesan":"login gagal 3x"}',
)

A message with the routing key log.warning.auth will enter queues bound with log.warning.*, log.#, or log.*.auth, depending on the binding pattern. This topic hierarchy means adding a new category doesn't require changing producer code — just add a binding.

Event Categorization and Dynamic Routing

A common pattern in e-commerce: the topics order.created, order.paid, order.shipped, and order.cancelled. Analytics just binds order.# to see all events, while the shipping service only binds order.shipped. Producers don't need to know who is listening — routing is fully controlled by bindings.

Headers Exchange

Header-Based Routing

A headers exchange matches messages based on the headers (key-value pairs) sent with the message, not the routing key. Each binding on a headers exchange declares arguments that must match the message headers.

Bind a headers exchange
rabbitmqadmin declare exchange name=header_ex type=headers
rabbitmqadmin declare queue name=critical_guest durable=true
rabbitmqadmin declare binding source=header_ex destination=critical_guest \
  arguments='{"x-match":"all","severity":"critical","source":"guest"}'

A message only enters critical_guest if it satisfies all the binding criteria, because x-match is set to all.

x-match: all vs any

Two matching modes for a headers exchange:

  • x-match: all — the message must match all headers declared in the binding.
  • x-match: any — the message only needs to match one of the headers.
PythonPublish with headers
channel.basic_publish(
    exchange="header_ex",
    routing_key="",
    body=b"pesan penting",
    properties=pika.BasicProperties(headers={"severity": "critical", "source": "guest"}),
)

Headers Exchange Performance Considerations

A headers exchange uses a heavier per-message evaluation than a topic, because the broker must compare every header of each message. For simple routing loads based on a single attribute, a topic exchange is more efficient and easier to understand. Use headers only when routing requires many criteria at once.

Tip

Industry standard: topic naming conventions use nouns and past-tense verbs, like order.created or payment.failed. Naming consistency determines how easily a system is understood by the team.

Conclusion

In episode 7 you mastered topic exchanges with the * and # wildcards, designed topic hierarchies for multi-criteria routing like logs based on severity and module, and understood headers exchanges with x-match all and any.

Key takeaways:

  • * matches one word; # matches zero or more words.
  • Dotted routing keys are the standard convention for topic exchanges.
  • A single queue can be bound with many patterns at once.
  • Producers don't need to know their consumers — routing is controlled by bindings.
  • Headers exchanges match headers, not routing keys.
  • x-match: all demands all criteria; any only needs one.
  • Topics are more efficient and easier to understand than headers for common cases.

In the next episode we will build the RPC pattern with RabbitMQ — using correlation ids and reply-to queues to request computation from another service and wait for the answer, including the Direct Reply-To feature and guidance on when you should not use this pattern. Prepare two terminals, because you will write an RPC client and server!