Add the rabbitmq module

This commit is contained in:
2026-08-29 09:58:34 +05:30
parent 3a682e496e
commit de9fc5ce4c
27 changed files with 1523 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
[← 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)