1
0
Files
spring-messaging-demo/broker-comparison
Ankur Mhatre e7e64a047b Add the broker-comparison module
Kafka, RabbitMQ and Pulsar measured side by side on ordering, replay and
consumer scaling by driving the three client libraries directly, plus an
operational-footprint measurement of each broker's own distribution.

Nine tests, three brokers started without Docker, and every number in the
documentation regenerated by scripts/run-all.sh.
2026-09-01 23:27:08 +05:30
..
2026-09-01 23:27:08 +05:30
2026-09-01 23:27:08 +05:30
2026-09-01 23:27:08 +05:30
2026-09-01 23:27:08 +05:30
2026-09-01 23:27:08 +05:30

broker-comparison

Companion project for Kafka vs RabbitMQ vs Pulsar for Java Teams: A Decision Framework with Benchmarks on ankurm.com.

Three brokers, three questions, one set of measurements. Ordering, replay and consumer scaling are asked identically of all three by driving the client libraries directly, so that what is compared is the brokers' delivery models rather than three sets of Spring defaults. What each Spring integration adds on top is chapter 6.

Verified stack

Component Version Source of the number
JDK (tests) 25.0.4.1+1 (Temurin) java -version
JDK (brokers) 21.0.12.1+1 (Temurin) java -version
Spring Boot 4.1.1 maven-metadata.xml on Maven Central
kafka-clients 4.2.1 spring-boot-dependencies-4.1.1.pom
amqp-client 5.30.0 spring-boot-dependencies-4.1.1.pom
pulsar-client 4.2.4 spring-boot-dependencies-4.1.1.pom
Kafka broker 4.2.1 (KRaft) kafka_2.13-4.2.1.tgz
RabbitMQ broker 3.10.25 generic-unix tarball
Pulsar broker 4.2.4 standalone apache-pulsar-4.2.4-bin.tar.gz

RabbitMQ is pinned at 3.10.25 because the box that produced these transcripts has Erlang 24; 3.11 and later need Erlang 25. The AMQP 0-9-1 semantics measured here are unchanged in 4.x.

Quickstart

The Kafka measurements need no external broker — spring-kafka-test starts a real KRaft broker in-process. The other two need a broker each, and the scripts start both without Docker and without root.

mvn -Dgroups=kafka test

ERL_ROOT=... RABBITMQ_HOME=... scripts/rabbit-broker.sh
mvn -Dgroups=rabbit test

PULSAR_HOME=... scripts/pulsar-broker.sh
mvn -Dgroups=pulsar test

scripts/footprint.sh kafka|rabbit|pulsar     # the operational numbers
scripts/run-all.sh                           # regenerates every docs/output/ file

Documentation

Chapter What it settles
01 Three models Where a message lives, and why everything else follows
02 Ordering What survives the second consumer, in all three
03 Replay Reading it twice, and the one broker that cannot
04 Consumer scaling The partition ceiling, and the client buffer that defeats fan-out in the other two
05 Operational footprint Startup, memory, ports, processes, configuration surface
06 What Spring adds The three integrations, and the Boot 4 starter trap
07 The decision table Every row backed by a measurement, and how to choose

Captured output

File What it shows
kafka-ordering.txt Per-key order kept, global order not — and why A, B and C were the wrong keys to demonstrate it
kafka-replay.txt The same 12 records read three times
kafka-consumer-scaling.txt 5 consumers, 3 partitions, 2 idle
rabbit-ordering.txt Message 1 finishing tenth
rabbit-replay.txt 12 messages, then 0, then an empty queue
rabbit-consumer-scaling.txt 40/0/0/0/0 without basicQos, 8 each with it
pulsar-ordering.txt Shared spreading a key, Key_Shared pinning it
pulsar-replay.txt seek(earliest) and a new subscription
pulsar-consumer-scaling.txt The 1000-message receiver queue defeating fan-out
footprint.txt Startup, memory, ports and config surface for all three
tests.txt The nine tests behind all of the above

What is not measured here

Throughput. A messages-per-second number from one 2-core container says nothing about any of these brokers, and publishing one would be worse than publishing nothing. The measurements above are structural: they hold on any hardware, because they are properties of the delivery models rather than of the machine.