How do you integrate Spring Boot with RabbitMQ (@RabbitListener, RabbitTemplate)?
Quick Answer
spring-amqp/spring-rabbit provides RabbitTemplate for sending messages to an exchange (with a routing key) and @RabbitListener for consuming messages from a queue, configured declaratively via application.yml and simple @Bean definitions for exchanges/queues/bindings. Unlike Kafka's topic-and-consumer-group model, RabbitMQ's routing is built around exchanges (which decide how a published message gets routed) and queues (which actually hold messages for consumers) connected by bindings — giving flexible routing patterns (direct, topic, fanout) suited to more traditional task-queue and pub-sub messaging scenarios.
Detailed Answer
spring-amqp/spring-rabbit provides Spring-friendly abstractions over RabbitMQ, following the AMQP model of exchanges, queues, and bindings rather than Kafka's partitioned-topic-and-consumer-group model.
Declaring the topology (exchange, queue, and the binding connecting them) as beans:
@Configuration
class RabbitConfig {
@Bean
Queue orderQueue() { return new Queue("order.queue", true); } // durable queue
@Bean
TopicExchange orderExchange() { return new TopicExchange("order.exchange"); }
@Bean
Binding binding(Queue orderQueue, TopicExchange orderExchange) {
return BindingBuilder.bind(orderQueue).to(orderExchange).with("order.placed.*");
}
}
Producing messages with RabbitTemplate:
@Service
class OrderEventPublisher {
private final RabbitTemplate rabbitTemplate;
void publish(OrderPlacedEvent event) {
rabbitTemplate.convertAndSend("order.exchange", "order.placed.created", event);
// exchange name, routing key, and the message payload (auto-converted, typically to/from JSON)
}
}
Consuming messages with @RabbitListener:
@Component
class OrderEventConsumer {
@RabbitListener(queues = "order.queue")
void handleOrderPlaced(OrderPlacedEvent event) {
inventoryService.reserveStock(event.orderId());
// acknowledged automatically on successful return, by default
}
}
Key conceptual difference from Kafka — routing via exchanges: RabbitMQ's exchange is the component that decides how an incoming published message gets routed to zero, one, or multiple queues, based on the exchange type and the message's routing key:
- Direct exchange: routes to queues bound with an exact matching routing key.
- Topic exchange: routes based on wildcard-pattern matching against the routing key (
order.placed.*matchingorder.placed.created,order.placed.updated, etc.) — used in the example above. - Fanout exchange: routes a message to every queue bound to it, regardless of routing key — a simple broadcast/pub-sub pattern.
This gives RabbitMQ very flexible, fine-grained routing topologies, in contrast to Kafka's simpler (but very high-throughput, replay-capable) topic-and-partition model.
When RabbitMQ tends to fit well: traditional task-queue/work-distribution scenarios, complex routing requirements (multiple consumer types needing different subsets of messages based on routing patterns), and cases valuing RabbitMQ's mature per-message acknowledgment and flexible dead-lettering/retry semantics over Kafka's strengths in extremely high-throughput, replayable event-log style streaming.