Journal
Sections
Reading settings
No voice for this language in this browser.
More voices
Code and English terms
Off: the page’s voice reads everything, with no pause between two voices.
No voice for this language in this browser.
More voices
More voices: Microsoft Edge has natural online voices, and Windows (Settings › Time & language › Speech), macOS and Android install others.
Progress
Section titled “Progress”- Lesson 1 — Getting started
- Lesson 2 — Topology: exchanges, bindings and queues
- Lesson 3 — Clients in C#, Java and Spring AMQP
- Lesson 4 — Reliability: acknowledgements, prefetch, confirms
- Lesson 5 — Queue types: classic, quorum and streams
- Lesson 6 — Errors: dead lettering, TTL, retries and delayed messages
- Lesson 7 — Patterns: work queues, publish/subscribe, RPC, competing consumers, outbox
- Lesson 8 — Clustering and high availability
- Lesson 9 — Security: virtual hosts, users, permissions, TLS
- Lesson 10 — Observability: Prometheus, Grafana, alarms, flow control
- Lesson 11 — Performance and sizing
- Lesson 12 — Other protocols: streams, MQTT, AMQP 1.0; RabbitMQ and Kafka
- Lesson 13 — Operations: upgrades, definitions, Kubernetes
2026-09-15 — Lessons 1 to 4
Section titled “2026-09-15 — Lessons 1 to 4”- The release information page listed 4.3.5 as the latest release on 2026-09-15, and Docker Hub’s
rabbitmq:4.3.5-managementtag was built on 2026-09-10. The image runs Erlang/OTP 27.3.4.17. The clients are the latest on NuGet and Maven Central that day: RabbitMQ.Client 7.2.2 and amqp-client 5.35.0. Spring Boot 4.1.1 manages amqp-client 5.30.0 and Spring AMQP 4.1.1; the course’s parent POM raises the client to 5.35.0 with therabbit-amqp-client.versionproperty, so both Java programs use the same client. - The code is one C# project with one command per example (
csharp) and a Maven build with two modules, a shaded JAR for the Java client and a Spring Boot JAR (java).check.shruns 36 steps against a fresh broker, includingrabbitmqctlandrabbitmq-diagnosticscommands and a broker restart, and compares each output withexpected/. I ran it twice in a row on fresh containers on Windows with the same results. - CI: GitHub-hosted Windows and macOS runners have no Docker, so the workflow builds the clients there and runs everything against a RabbitMQ service container on Linux only.
- To get deterministic output from a broker, the programs avoid timing: they read queues with
basic.getafter publishing, stop consumers after a known count, and name no server-generated consumer tags. Two examples still wait a little: 300 ms after a simulated crash, so that the prefetch window is filled, and 500 ms to check that a consumer receives no fourth message. - None of the GuitarAlchemist repositories uses RabbitMQ in its code (ga at
dc2e74cb, ix ated5e998, tars at54487423). The only configuration I found is a generated Docker Compose file in tars: it runsrabbitmq:3.12-management, a series no longer on the supported list, with a data volume but nohostname, so a recreated container would start under a new node name and not find its data, and it hard-codes the credentialsadmin/admin123. Its health check,rabbitmq-diagnostics pingas root, is the pattern that crashed my broker, although a 30-second interval makes the race unlikely.
The broker that stopped before it started. My first broker.sh waited for the broker by running docker exec rabbitmq rabbitmq-diagnostics check_port_connectivity every second. The container died within two seconds with Error when reading /var/lib/rabbitmq/.erlang.cookie: eacces. The CLI, running as root, created the Erlang cookie before the node did; the node, running as rabbitmq, couldn’t read it. I reproduced it with a single docker exec right after docker run, and ls -la showed the file owned by root. Running the container with --user rabbitmq fixed it, with a health check every second. It is docker-library/rabbitmq#695, closed without a comment in 2024; the image’s documentation doesn’t mention it. Lesson 1 tells the story.
Surprises:
- RabbitMQ 4.3.5 refuses a queue declared with
durable: falseunless it is exclusive:541 INTERNAL_ERROR - Feature transient_nonexcl_queues is deprecated, a connection error. Every older tutorial that declaresdurable: falsefails on this version. - That error text ends with
To...: AMQP reply texts are short strings of at most 255 bytes, and the deprecation warning is longer. - An unroutable message published without
mandatoryis confirmed. A publisher confirm says the broker is done with the message, not that a queue has it. - With confirm tracking, RabbitMQ.Client 7 throws
PublishReturnExceptionfor a returned message; the Java client reports the same publish as confirmed and calls theReturnListenerjust before. redelivereddepends on the prefetch window: with no limit, messages a crashed consumer never looked at come back withredelivered=True.- Spring AMQP’s Jackson converter writes the Java class name,
dev.learn.rabbitmq.spring.OrdersApplication$Order, in a__TypeId__header, and ignores its absence when the listener’s parameter type is concrete. - Right after a broker started,
curl "…/api/queues/%2F/hello?columns=name,messages,consumers"returned only{"name":"hello"}. In another run, six seconds after publishing, the same request returned{"consumers":0,"messages":2,"name":"hello"}. The management plugin collects queue statistics periodically, so the HTTP API lags a few seconds behindrabbitmqctl. The exact interval on 4.3.5 is to verify. - With nothing listening on port 5672 on Windows, the .NET client took 4.3 seconds to report
BrokerUnreachableException, the Java client 165 milliseconds. A fraction of a second afterdocker rm -f, Docker Desktop still accepted the TCP connection and the .NET client reportedconnection.start was never receivedinstead.
Things I got wrong first:
- Git Bash converted
/var/lib/rabbitmq/indocker exec rabbitmq ls /var/lib/rabbitmq/into a Windows path underC:/Program Files/Git.MSYS_NO_PATHCONV=1stops it;check.shsets it for commands run in the container. Setting it for the whole script broke Maven’s launcher script instead. - The Spring Boot parent POM configures the Maven Shade plugin with its own transformers, and Maven merged my
ManifestResourceTransformerinto one of them: “Cannot find ‘resource’ in class ManifestResourceTransformer”.combine.self="override"on<transformers>fixed it. - The first version of the queue dumps printed each message’s routing key before its body. Headers and fanout messages have an empty or meaningless key, and the output had double spaces; the dumps now print bodies, and the topic examples use the routing key as the body.
- My first push was refused: the GitHub token no longer had the
workflowscope, needed to create a file under.github/workflows. The code went first; the workflow waits for the scope, so until then CI doesn’t run these programs.
Open questions
Section titled “Open questions”- The
wslcand Podman commands of lesson 1, and the Linux and macOS commands, are to verify; CI runs the image on Linux. - Automatic connection recovery in both clients after a broker restart: to verify with a program, probably in lesson 8.
- What an exception thrown from a RabbitMQ.Client 7
ReceivedAsynchandler does to the delivery, and the conversion error of a Spring listener with an interface parameter and no__TypeId__: to verify in lesson 6. deprecated_features.permit.transient_nonexcl_queues = trueto allow non-durable queues again on 4.3.5: to verify.- Where the 4.3 seconds of the .NET client’s refused connection go on Windows: to verify.
- Throughput of persistent versus transient messages, and of per-message versus batched confirms: to verify in lesson 11.