Exchanges and Routing
How RabbitMQ exchange types (direct, fanout, topic, headers) work as virtual connector-side routing, resolved at publish time into KubeMQ Queue channels.
In the KubeMQ RabbitMQ connector, exchanges and bindings are virtual connector-side routing
metadata, not data stores. There is no exchange object holding messages — a publish to an
exchange is resolved into a set of target queues at publish time, and each resolved queue is
written to its KubeMQ Queue channel amqp.{vhost}.{queue}. This guide covers the exchange types,
topic wildcards, declare idempotency, and what happens to the routing result.
Every AMQP queue maps to a single KubeMQ Queue channel amqp.{vhost}.{queue}. Exchanges and
bindings exist only to select which queues a publish lands in — they are evaluated by the
connector at publish time, then discarded. See
Architecture.
How a publish routes
The publish is first authorized on the exchange (Write, before routing), then the matcher
produces a set of target queues, deduplicates it, and writes the message to each queue's KubeMQ
channel — where the shared queue layer every KubeMQ protocol goes through authorizes each
destination once more.
Exchange types
| Exchange type | Routing semantics | Pre-declared (per vhost) |
|---|---|---|
default ("") | Implicit per-queue binding; routing-key = queue name; a missing queue is unroutable | implicit |
| direct | Exact routing-key match; multiple bindings on the same key all match | amq.direct (durable) |
| fanout | All bound queues; routing key ignored | amq.fanout (durable) |
| topic | Trie matcher; * = exactly one word, # = zero or more words, . = word separator | amq.topic (durable) |
| headers | x-match ∈ any-with-x (default all); x--prefixed headers are excluded from matching | amq.headers / amq.match (durable) |
| x-delayed-message | The delayed-message plugin's type; routes by the x-delayed-type argument (one of the four above). A missing or non-string x-delayed-type → 406; one naming an unknown type → 503. The x-delay header delays delivery on every exchange, plugin or not | — |
Pre-declared amq.* exchanges
The amq.direct, amq.fanout, amq.topic, amq.headers, and amq.match exchanges are
pre-declared durable per vhost, together with the internal topic exchange amq.rabbitmq.trace;
the default vhost additionally carries the internal amq.rabbitmq.log, matching RabbitMQ 4.3.4.
They:
- cannot be deleted by a client →
403 access-refused; - cannot be redeclared with different args →
406 precondition-failed; - a client declaring any exchange with an
amq.*prefix →403.
The two amq.rabbitmq.* exchanges exist so tooling that declares or binds to them keeps working.
A direct publish to either is refused 403, and nothing is ever routed through them — there is
no firehose tracing and no log forwarding; a queue bound to either stays empty.
Exchange-to-exchange bindings and auto-delete
exchange.bind / exchange.unbind work with the semantics measured on RabbitMQ 4.3.4: a
message published to the source is routed on through the destination exchange's own bindings
with its routing key and headers unchanged; a queue reachable by several paths receives one
copy; cycles and an exchange bound to itself are legal (each exchange is visited once per
publish); an internal exchange receives through a binding — internal forbids only a direct
publish. The default exchange is refused at either end (403); a missing exchange is 404 on
bind, while exchange.unbind succeeds even when an end does not exist. Permissions follow
RabbitMQ's pair: write on the destination, read on the source.
An exchange declared auto-delete is removed when its last binding goes — by
queue.unbind, by the bound queue's deletion, or by an exclusive owner's connection closing —
and one that has never been bound stays. if-unused on exchange.delete counts bindings
from the exchange.
Topic wildcards
The topic matcher uses . as the word separator:
| Token | Matches |
|---|---|
* | exactly one word |
# | zero or more words |
Worked examples:
| Binding pattern | Matches | Does NOT match |
|---|---|---|
stock.*.nyse | stock.ibm.nyse | stock.ibm.us.nyse (two words for *) |
stock.# | stock, stock.a, stock.a.b | stocks.a |
# | any key, including the empty key | — |
*.orange.* | quick.orange.rabbit | lazy.orange.elephant.x |
Bindings and declare idempotency
- Declare idempotency: identical args → ok; any field mismatch (type / durable /
auto-delete / internal / deep-equal arguments) →
406. - Passive declare (
passive=true): exists → ok; missing →404 not-found.
The routing result
- The publish is authorized on the exchange —
Write, checked before routing and before the exchange's existence is resolved. A denial closes the channel with403naming the exchange (audited asamqp.publish.denied); the publish is never acknowledged and never returned as312. See Authentication. - The exchange matcher produces a set of target queues, following exchange-to-exchange
bindings and, when nothing matched, the exchange's
alternate-exchange. - The set is deduplicated — a message matching multiple bindings to the same queue is delivered exactly once.
- If the routed set is empty:
mandatory=true→basic.return(312 NO_ROUTE)with the full message content;- otherwise the message is silently dropped.
- Each destination queue is authorized once more by the shared queue layer every KubeMQ
protocol goes through. A refusal there fails the whole publish as one outcome — a
basic.nackin confirm mode, a541channel close without confirms — never a partial delivery to the permitted queues.312is reserved for a publish that was permitted and matched no binding.
See Reliability for mandatory / basic.return.
Alternate exchanges
alternate-exchange on exchange.declare is honored: an exchange whose own bindings match
nothing hands the message to the exchange its alternate-exchange argument names, which may have
an alternate of its own; an internal exchange works as an alternate; a missing alternate or an
alternate cycle is simply unroutable (a mandatory publish is returned 312, the channel stays
open); and a match on an exchange-to-exchange binding that leads to no queue still counts as a
match, so the alternate does not fire — all as measured on RabbitMQ 4.3.4. Declaring a new
exchange with the argument needs read on the exchange itself and write on the alternate it
names.
Consumer-side arguments that shape delivery
x-single-active-consumer: trueonqueue.declare: only the first-registered consumer receives; the next in registration order takes over when it cancels or its connection drops (and gets whatever it left unacknowledged); a later consumer never pre-empts the active one, whatever its priority. Held across the cluster by node presence.x-priorityonbasic.consume: while a higher-priority consumer has prefetch room it receives every message, lower-priority consumers get only the overflow, and equal priorities share round-robin. A non-integer is406. Priorities are arbitrated per node.
Inert arguments. Only x-max-length-bytes, x-queue-type / x-queue-mode and
x-max-priority are accepted-and-ignored (badged in the dashboard topology view). Every other
argument that older versions of this page listed as inert — alternate-exchange,
x-max-length / x-overflow, x-single-active-consumer, x-expires, consumer x-priority —
is now enforced, and x-expires deletes an idle queue. See
Capabilities.
Error quick reference
| Trigger | Code |
|---|---|
Unmatched direct routing key, no mandatory | silent drop |
mandatory=true and unroutable | 312 |
| Redeclare with mismatched args | 406 |
| Passive declare of a missing exchange | 404 |
Client declares an amq.* exchange / deletes a pre-declared exchange / publishes directly to an internal exchange | 403 |
| Publish denied on the exchange | 403 (channel closed; no ack, no 312) |
exchange.bind / exchange.unbind naming the default exchange | 403 |
exchange.bind naming a missing exchange | 404 |
x-delayed-message without a string x-delayed-type | 406 |
Related
Queues and consumers
Declaring queues, consuming, ack/nack, and prefetch on the amqp.{vhost}.{queue} channels that routing resolves to.
Reliability
Publisher confirms, mandatory/return, dead-letter exchanges, and the publish-then-close footgun.
Channel mapping
The amqp.{vhost}.{queue} naming grammar and how vhosts, queues, and routing keys map to KubeMQ channels.
Was this page helpful?