13.2 Message Queue Fundamentals and RabbitMQ Integration
A Message Queue (MQ) is middleware enabling asynchronous communication between services. Instead of Service A waiting for Service B's response, it places a message in the queue and moves on — Service B consumes it at its own pace. This dramatically reduces inter-service coupling.
1. AMQP and Exchange-Queue Routing Concepts
RabbitMQ operates on the AMQP protocol. Messages always flow in this order: Producer → Exchange → Binding → Queue → Consumer.
- Exchange Types:
Direct: Routes to queues where the routing key matches exactly (1:1)Topic: Routes to multiple queues using wildcard (*,#) pattern matchingFanout: Broadcasts to all bound queues (1:N)
2. Quick Spring AMQP Setup
implementation 'org.springframework.boot:spring-boot-starter-amqp'
@Configuration
public class RabbitMqConfig {
public static final String QUEUE_NAME = "order.queue";
public static final String EXCHANGE_NAME = "order.exchange";
public static final String ROUTING_KEY = "order.created";
@Bean
public Queue orderQueue() {
return new Queue(QUEUE_NAME, true); // durable=true: survives broker restarts
}
@Bean
public DirectExchange orderExchange() {
return new DirectExchange(EXCHANGE_NAME);
}
@Bean
public Binding binding(Queue orderQueue, DirectExchange orderExchange) {
return BindingBuilder.bind(orderQueue).to(orderExchange).with(ROUTING_KEY);
}
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter(); // Serializes Java objects to JSON
}
}
3. Publishing (Producer) and Consuming (Consumer)
// [Producer] Publishing a message
@Service
@RequiredArgsConstructor
public class OrderProducer {
private final RabbitTemplate rabbitTemplate;
public void sendOrderCreatedEvent(OrderCreatedEvent event) {
rabbitTemplate.convertAndSend(
RabbitMqConfig.EXCHANGE_NAME,
RabbitMqConfig.ROUTING_KEY,
event
);
log.info("Order event published: {}", event.getOrderId());
}
}
// [Consumer] Consuming from the queue
@Service
public class OrderConsumer {
@RabbitListener(queues = RabbitMqConfig.QUEUE_NAME)
public void handleOrderCreated(OrderCreatedEvent event) {
log.info("Received order event: {}", event.getOrderId());
// Async post-processing: send email, deduct inventory, etc.
}
}
warning
If an exception is thrown inside @RabbitListener, RabbitMQ will by default requeue the message — causing an infinite retry loop. Always configure a Dead Letter Queue (DLQ) or a RetryInterceptor with a maximum retry count to prevent this.