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.

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.
Three components determine topic matching:
* (star) matches exactly one word — the word must not contain a dot.# (hash) matches zero or more words.orders match themselves literally."log.*.error" → matches: log.api.error, log.worker.error
"log.#" → matches: log, log.api, log.api.error
"*.new.*" → matches: orders.new.eu, payments.new.usRules to remember: * cannot cross a dot, while # can. The binding log.# accepts every message starting with log., no matter how deep the topic is.
Let's practice. We create a topic exchange for routing logs with the pattern log.<severity>.<module>:
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.*.apiThe 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.
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:
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.
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.
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.
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.
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.channel.basic_publish(
exchange="header_ex",
routing_key="",
body=b"pesan penting",
properties=pika.BasicProperties(headers={"severity": "critical", "source": "guest"}),
)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.
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.x-match: all demands all criteria; any only needs one.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!