rabbitmq — exchanges, bindings and a dead-letter queue that works
Companion project for Spring Boot and RabbitMQ: Exchanges, Queues, Bindings and a Working Dead-Letter Queue on ankurm.com.
Ten tests against a real RabbitMQ broker. All four exchange types, manual acknowledgement,
a dead-letter path exercised through basicNack and through TTL expiry, and the three ways a
topology loses work quietly.
Versions
| Version | Notes | |
|---|---|---|
| JDK | 25 (Temurin 25.0.4.1+1) | current LTS |
| Spring Boot | 4.1.1 | |
| Spring AMQP / Spring Rabbit | 4.1.1 | Boot-managed |
com.rabbitmq:amqp-client |
5.30.0 | Boot-managed. Central has 5.35.0 |
| Testcontainers | 2.0.5 | artifact is testcontainers-rabbitmq, not rabbitmq |
| Broker for the committed transcripts | RabbitMQ 3.10.25 | see the note below |
Client-side versions were read from repo1.maven.org/.../maven-metadata.xml and Boot's
spring-boot-dependencies POM.
About the broker version. The machine that regenerates
docs/output/has no Docker daemon and no root, soscripts/broker.shstarts a generic-unix RabbitMQ against an extracted Erlang 24 runtime, and the newest release that pairs with Erlang 24 is 3.10.25. Everything exercised here — the four exchange types,x-dead-letter-exchange,x-dead-letter-routing-key,x-message-ttl,x-death, manual ack,mandatoryreturns,PRECONDITION_FAILEDon inequivalent arguments — is AMQP 0-9-1 behaviour that is unchanged in RabbitMQ 4.x. The one broker-version difference worth knowing for 4.x is that quorum queues are the default queue type and classic mirrored queues are gone; nothing in this module declares a queue type, so the topology is valid on both. If you have Docker, run the same tests againstrabbitmq:4.1-managementwithTestcontainersConfiguration.
Quickstart
# with Docker: point the tests at Testcontainers, or run your own broker on 5672
mvn test
# without Docker:
export ERL_ROOT=/path/to/erlang RABBITMQ_HOME=/path/to/rabbitmq_server-3.10.25
./scripts/run-all.sh
Documentation
- The on-ramp
- Four exchange types
- The message that goes nowhere and says nothing
- A dead-letter queue that actually works
- Acknowledgement
- Changing your mind about a queue
Captured output
| File | Shows |
|---|---|
topic-wildcards.txt |
* vs # over five routing keys |
dead-letter.txt |
x-death for rejected and for expired |
requeue-loop.txt |
199 redeliveries, 0 dead-lettered |
unroutable.txt |
312 NO_ROUTE |
precondition-failed.txt |
406 on an inequivalent argument |
tests.txt |
10 tests |
Five things this module exists to prove
- An unroutable message is discarded silently, and finding out needs
publisher-returnsandmandatory— two settings in two different places. Setting onlymandatorydoes nothing. requeue=truenever dead-letters. 200 attempts, 199 redeliveries, an empty DLQ and a queue depth that stays at 1 the whole time.x-death.reasondistinguishesrejectedfromexpired, which is the difference between a consumer that refused the work and a consumer that never got to it.- Queue arguments are immutable —
406 PRECONDITION_FAILED, wrapped in an exception whose own message is the stringjava.io.IOException. - Boot auto-configures no JSON converter for RabbitMQ, and the converter you want is
JacksonJsonMessageConverter, notJackson2JsonMessageConverter.