[← The on-ramp](01-the-on-ramp.md) · [Module README](../README.md) · [The silent drop →](03-the-silent-drop.md) # 2. Four exchange types A producer never publishes to a queue. It publishes to an **exchange** with a **routing key**, and bindings decide where that lands. The whole topology is in [`Topology.java`](../src/main/java/com/ankurm/rabbit/Topology.java) as beans; `RabbitAdmin` declares them when the connection opens. ## Direct — exact match Binding key `new` receives routing key `new`. Nothing else. This is the workhorse: one queue per command type. ## Fanout — routing key ignored entirely Every bound queue gets a copy. `audit.all` and `analytics.all` both receive it, and the routing key you passed is not consulted at all. Use it for broadcast; use it knowing that adding a queue adds a full copy of the traffic. ## Topic — wildcards over dot-separated words `*` is **exactly one word**. `#` is **zero or more**. The distinction is the one people get wrong, so here it is against a real broker ([`docs/output/topic-wildcards.txt`](output/topic-wildcards.txt)): ``` routing key orders.eu orders.high note order.eu.high true true matches both order.eu.low true false matches order.eu.* only order.us.high false true matches order.#.high only order.eu.west.high false true matches order.#.high only - * is one word order.high false true matches order.#.high - # can be zero words ``` Bindings are `order.eu.*` and `order.#.high`. Two rows are worth pausing on: - `order.eu.west.high` does **not** match `order.eu.*`, because `*` matches one word and `west.high` is two. Regex intuition says otherwise. - `order.high` **does** match `order.#.high`, because `#` matches zero words. So a binding you wrote to mean "something in the middle" also matches "nothing in the middle". Design routing keys most-general-to-most-specific (`order.eu.west.high`, not `high.order.eu.west`), because `#` and `*` work left to right and a hierarchy you can bind usefully is one that starts broad. ## Headers — match a map, ignore the routing key `x-match=all` requires every named header to be present **and equal**. `x-match=any` requires one. From `RoutingTest`: | Message headers | `x-match=all` queue | `x-match=any` queue | |---|---|---| | `priority=high, region=eu` | yes | yes | | `priority=high` | no | yes | | `priority=high, region=us` | no | yes | The third row is the one to remember: `all` matches on **value**, not on presence. A header that is there with the wrong value fails the same way a missing one does. Headers exchanges are slower than topic exchanges and much less common. Reach for them when the routing criteria are genuinely multi-dimensional and do not compose into a hierarchy. ## The default exchange Publishing to the empty exchange name `""` routes by **queue name**, using the routing key as the queue name. Every queue is implicitly bound to it. That is how `convertAndSend("", "orders.work", message)` works, and it is the one piece of AMQP that behaves like a magic constant. [The silent drop →](03-the-silent-drop.md)