Files
2026-08-29 10:30:21 +05:30

4.0 KiB

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, so scripts/broker.sh starts 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, mandatory returns, PRECONDITION_FAILED on 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 against rabbitmq:4.1-management with TestcontainersConfiguration.

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

  1. The on-ramp
  2. Four exchange types
  3. The message that goes nowhere and says nothing
  4. A dead-letter queue that actually works
  5. Acknowledgement
  6. 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

  1. An unroutable message is discarded silently, and finding out needs publisher-returns and mandatory — two settings in two different places. Setting only mandatory does nothing.
  2. requeue=true never dead-letters. 200 attempts, 199 redeliveries, an empty DLQ and a queue depth that stays at 1 the whole time.
  3. x-death.reason distinguishes rejected from expired, which is the difference between a consumer that refused the work and a consumer that never got to it.
  4. Queue arguments are immutable406 PRECONDITION_FAILED, wrapped in an exception whose own message is the string java.io.IOException.
  5. Boot auto-configures no JSON converter for RabbitMQ, and the converter you want is JacksonJsonMessageConverter, not Jackson2JsonMessageConverter.