JMS queues vs topics, point-to-point vs publish-subscribe, RabbitMQ vs Kafka, RabbitMQ acknowledgements, prefetch limits, reliable delivery (publisher confirms, durable queues, persistent messages, quorum queues), dead-letter exchanges, delayed and scheduled messages, Spring AMQP message converters, and transactional messaging in JMS.
Published September 25, 2026
Know when a message broker (RabbitMQ, ActiveMQ, SQS: smart routing, per-message acknowledgement, work queues) fits better than a log (Kafka: replayable streams, high throughput, ordering per partition). Senior answers compare their delivery guarantees, and their operational models.
Short answer:
Short answer:
| RabbitMQ | Kafka | |
|---|---|---|
| Model | Message broker: smart broker, simple consumers. Exchanges route to queues | Distributed commit log: a dumb broker, smart consumers (offsets) |
| Retention | Messages are removed once acknowledged (streams are an exception) | Retained by time or size. Replayable by any consumer group |
| Routing | Rich: direct, topic (wildcards), fanout, headers exchanges | Topic, plus partition by key. Routing logic lives in the consumers or Kafka Streams |
| Ordering | Per queue (competing consumers weaken it) | Per partition (by key) |
| Throughput | High (tens of thousands of messages per second per node); per-message overhead | Very high (millions per second), with batching and sequential I/O |
| Consumer semantics | Push with prefetch; per-message ack/nack, requeue, TTL, priority, delayed delivery | Pull; offset commits, per partition |
| Best for | Task queues, RPC, complex routing, per-message retries or priorities, low latency | Event streaming, event sourcing, CDC, analytics pipelines, replay, many independent consumers |
They're often used together: Kafka for the event backbone, and RabbitMQ or SQS for work queues. RabbitMQ Streams and quorum queues narrow the gap somewhat.
Learn it in depth → Messaging Technology Choices
Short answer:
basic.ack (remove it), basic.nack/basic.reject with requeue=true (redeliver) or requeue=false (drop, or dead-letter it if a DLX is configured);redelivered flag is set);AcknowledgeMode.AUTO (the container acks after the listener returns successfully, and nacks or rejects on an exception, with configurable requeue behaviour), MANUAL (channel.basicAck(tag, false)), or NONE.requeue=true: they loop forever. Set the delivery limits (quorum queues' x-delivery-limit), and dead-letter them.Short answer: Prefetch (basic.qos) caps how many unacknowledged messages the broker pushes to a consumer or channel at once. Spring's prefetchCount defaults to 250. Why it matters:
concurrency.It's RabbitMQ's back-pressure mechanism for consumers.
Short answer: Cover publisher → broker → consumer:
publisher-confirm-type: correlated, with a callback);publisher-returns, a ReturnsCallback);deliveryMode=2);Short answer: A dead-letter exchange (DLX) receives messages that a queue can't deliver or process:
requeue=false;x-message-ttl);x-max-length with the drop-head overflow policy);Configure it on the queue with the x-dead-letter-exchange (and optional x-dead-letter-routing-key) arguments. The DLX routes to a dead-letter queue, for inspection, alerting and replay. The x-death header records why, how many times, and where from. It's used for poison-message isolation, and (with TTL) for delayed retries.
Short answer:
rabbitmq_delayed_message_exchange): the x-delayed-message exchange type, with an x-delay header per message. It's simpler, but the delayed messages are stored on a single node (with limited HA and scale).x-delay header (MessageProperties.setDelay) with a delayed exchange, or declare the TTL/DLX queues as beans.Short answer: A MessageConverter turns Java objects into AMQP Message bodies plus properties, and back:
SimpleMessageConverter handles String, byte[] and Java-serialised objects. Avoid Java serialisation: it's insecure, and coupled to the classes;Jackson2JsonMessageConverter (the common choice) writes JSON, and sets content_type and type-id headers (__TypeId__). Configure a trusted packages or type mapper (DefaultJackson2JavaTypeMapper, with an allowed class list, or TypePrecedence.INFERRED from the listener's parameter type) to avoid deserialisation attacks and coupling to producer class names;ContentTypeDelegatingMessageConverter (chosen by content type).Register the converter on the RabbitTemplate and the listener container factory. @RabbitListener methods can then take typed payloads (void on(OrderPlaced event)) with @Payload/@Header arguments.
Short answer:
connection.createSession(true, …), or JmsTemplate.setSessionTransacted(true), or listener containers with sessionTransacted=true). Sends and receives within the session are committed or rolled back together (session.commit()/rollback()). On rollback, the received messages are redelivered.JmsTransactionManager, with @Transactional, for JMS-only transactions. DefaultMessageListenerContainer with sessionTransacted=true rolls back and redelivers on a listener exception.JtaTransactionManager, with Atomikos or Narayana), with XA-capable brokers and datasources. It's heavy, slower, and has operational pitfalls;Short answer: Choose Kafka when you need durable event streams consumed by many independent services, replay and reprocessing, high throughput, stream processing (Kafka Streams or Flink), or CDC pipelines. Choose RabbitMQ (or SQS) when you need work queues with per-message acknowledgement, retries, priorities or delays, complex routing, RPC-style request-reply, or lower operational weight for moderate volumes. Many platforms use both.
Q: What are quorum queues? A: RabbitMQ's replicated, durable queue type, based on Raft. They give data safety across node failures, poison-message handling (delivery limits), and predictable failover. They're the recommended default for durable workloads, replacing classic mirrored queues.
Q: How do competing consumers affect ordering in RabbitMQ?
A: With several consumers on one queue, messages are processed concurrently, so global ordering isn't preserved. For per-entity ordering, use consistent-hash exchanges or single active consumer (x-single-active-consumer), or partition across several queues by key.
Q: What is JMS 2.0's simplified API?
A: JMSContext (combining the connection and session), JMSProducer/JMSConsumer, method chaining, shared subscriptions, and asynchronous send. Jakarta Messaging continues it (jakarta.jms). Spring's JmsTemplate/JmsClient wrap it.
Q: How does Amazon SQS compare? A: It's a fully managed queue: at-least-once standard queues, or FIFO queues (ordering plus deduplication, with throughput limits), visibility timeouts instead of acks, DLQ redrive policies, and long polling. There are no brokers to run, but routing is limited (combine it with SNS for fan-out).