Introduction: Why We Need Message Middleware
Message Queue Comparison
RabbitMQ Core Concepts
Exchange Types Explained
Spring AMQP in Practice
Message Reliability Guarantees
Delayed Message Implementation
Best Practices & Common Issues
Summary
Introduction: Why We Need Message Middleware Pain Points of Synchronous Calls In microservices architecture, inter-service communication typically uses synchronous methods like OpenFeign. This “phone call” style communication is straightforward but has three core problems:
Problem
Symptom
Impact
Poor Extensibility
New features require modifying existing code
Violates Open-Closed Principle
Performance Degradation
Longer call chains increase total latency
Poor user experience
Cascading Failures
Downstream failures cause transaction rollback
System instability
Advantages of Asynchronous Communication Message middleware introduces asynchronous communication , similar to “sending messages”:
Decoupling : Publishers don’t need to know message receivers
Load Leveling : Smooths burst traffic, protecting downstream systems
Fault Tolerance : Single service failure doesn’t break the flow
Scalability : Add consumers by simply subscribing to queues
Message Queue Comparison Mainstream MQ Comparison
Feature
ActiveMQ
RabbitMQ
RocketMQ
Kafka
Availability
Average
High
Very High
Very High
Reliability
Average
High
High
Medium
Throughput
10K/s
10K/s
100K/s
Million/s
Latency
ms
μs
ms
ms
Community
Low
High
Medium
High
Selection Recommendations
Low Latency : RabbitMQ, Kafka
High Reliability : RabbitMQ, RocketMQ
High Throughput : RocketMQ, Kafka
Quick Start : RabbitMQ (intuitive management UI, extensive documentation)
RabbitMQ offers balanced performance across all aspects, making it especially suitable for small and medium-sized enterprises to quickly adopt message queue solutions.
RabbitMQ Core Concepts Architecture Overview 1 2 3 4 5 6 7 8 9 10 ┌─────────────┐ ┌──────────┐ ┌─────────────┐ │ Publisher │────▶│ Exchange │────▶│ Queue │ │ (Producer) │ │ │ │ │ └─────────────┘ └──────────┘ └──────┬──────┘ │ ▼ ┌─────────────┐ │ Consumer │ │ │ └─────────────┘
Core Components
Component
Function
Characteristics
Exchange
Receives and routes messages to queues
No storage capability
Queue
Stores messages
Where message persistence happens
Binding
Links exchanges to queues
Determines routing rules
Virtual Host
Logical isolation unit
Like namespaces
Docker Quick Start 1 2 3 4 5 6 7 docker run \ -e RABBITMQ_DEFAULT_USER=admin \ -e RABBITMQ_DEFAULT_PASS=123456 \ --name mq \ -p 15672:15672 \ -p 5672:5672 \ -d rabbitmq:3.8-management
15672: Management console port
5672: AMQP protocol port
Exchange Types Explained 1. Fanout (Broadcast) Characteristics : Routes messages to all bound queues
1 2 3 4 5 6 7 8 @Bean public FanoutExchange fanoutExchange () { return new FanoutExchange ("order.fanout" ); } rabbitTemplate.convertAndSend("order.fanout" , "" , message);
Use Cases : Notifications, log broadcasting
2. Direct (Point-to-Point) Characteristics : Exact RoutingKey matching
1 2 3 4 5 6 @RabbitListener(bindings = @QueueBinding( value = @Queue(name = "order.queue"), exchange = @Exchange(name = "order.direct", type = ExchangeTypes.DIRECT), key = {"pay.success", "pay.refund"} ))
Use Cases : Targeted message distribution
3. Topic (Pattern Matching) Characteristics : Supports wildcard patterns
Wildcard
Meaning
Example
#
Matches 0 or more words
order.# matches order.create, order.pay.success
*
Matches exactly 1 word
order.* only matches order.create
1 2 3 key = "order.#" key = "#.error"
Use Cases : Complex routing rules, log levels
Spring AMQP in Practice Dependency Configuration 1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-amqp</artifactId > </dependency >
Basic Configuration 1 2 3 4 5 6 7 8 9 10 11 spring: rabbitmq: host: localhost port: 5672 virtual-host: / username: admin password: ***** listener: simple: prefetch: 1 acknowledge-mode: auto
Producer Example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Service public class OrderProducer { @Autowired private RabbitTemplate rabbitTemplate; public void sendOrder (Order order) { rabbitTemplate.convertAndSend( "order.exchange" , "order.created" , order, message -> { message.getMessageProperties() .setDeliveryMode(MessageDeliveryMode.PERSISTENT); return message; } ); } }
Consumer Example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Component @Slf4j public class OrderConsumer { @RabbitListener(bindings = @QueueBinding( value = @Queue(name = "order.queue", durable = "true"), exchange = @Exchange(name = "order.exchange", type = ExchangeTypes.TOPIC), key = "order.created" )) public void handleOrder (Order order) { log.info("Received order message: {}" , order.getId()); } }
WorkQueues (Work Distribution) Multiple consumers competing for messages from the same queue:
1 2 3 4 5 6 7 8 9 10 11 12 @RabbitListener(queues = "work.queue") public void consumer1 (String msg) { log.info("Consumer1 processing: {}" , msg); } @RabbitListener(queues = "work.queue") public void consumer2 (String msg) throws InterruptedException { Thread.sleep(1000 ); log.info("Consumer2 processing: {}" , msg); }
Configure prefetch for fair dispatch :
1 2 3 4 5 spring: rabbitmq: listener: simple: prefetch: 1
Message Reliability Guarantees Producer Reliability 1. Publisher Confirm 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 spring: rabbitmq: publisher-confirm-type: correlated publisher-returns: true @Bean public void init () { rabbitTemplate.setReturnsCallback(returned -> { log.error("Message routing failed: {}" , returned.getMessage()); }); } public void sendWithConfirm (String exchange, String routingKey, Object msg) { CorrelationData cd = new CorrelationData (); cd.getFuture().addCallback( confirm -> { if (confirm.isAck()) { log.info("Message sent successfully" ); } else { log.error("Message send failed: {}" , confirm.getReason()); } }, ex -> log.error("Send exception" , ex) ); rabbitTemplate.convertAndSend(exchange, routingKey, msg, cd); }
MQ Reliability Data Persistence
Level
Configuration
Description
Exchange Persistence
durable = true
Exchange survives restart
Queue Persistence
durable = true
Queue survives restart
Message Persistence
deliveryMode = 2
Message written to disk
Lazy Queues Starting from version 3.12, all queues use lazy queue mode by default:
1 2 3 4 5 6 7 @Bean public Queue lazyQueue () { return QueueBuilder .durable("lazy.queue" ) .lazy() .build(); }
Advantages :
Messages stored on disk directly, no memory pressure
Supports millions of message backlogs
Consumer Reliability Consumer Acknowledgment Modes 1 2 3 4 5 spring: rabbitmq: listener: simple: acknowledge-mode: auto
Mode
Description
Use Case
none
Auto-ack, delete on arrival
Testing only
auto
Auto-ack on success, retry on failure
Recommended for production
manual
Manual ack control
Fine-grained control needed
Consumer Retry and Failure Handling 1 2 3 4 5 6 7 8 9 spring: rabbitmq: listener: simple: retry: enabled: true initial-interval: 1000ms multiplier: 2 max-attempts: 3
Recovery strategy after retries exhausted :
1 2 3 4 5 6 7 8 9 @Bean public MessageRecoverer republishMessageRecoverer () { return new RepublishMessageRecoverer ( rabbitTemplate, "error.exchange" , "error.routingKey" ); }
Idempotency Guarantees Messages may be consumed repeatedly; ensure idempotent business logic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Bean public MessageConverter messageConverter () { Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter (); converter.setCreateMessageIds(true ); return converter; } @Override public void markOrderPaySuccess (Long orderId) { lambdaUpdate() .set(Order::getStatus, PAID) .eq(Order::getId, orderId) .eq(Order::getStatus, UNPAID) .update(); }
Delayed Message Implementation Solution 1: Dead Letter Exchange + TTL 1 2 3 4 Producer ──▶ ttl.queue (set expiration, no consumer) │ ▼ (expires and becomes dead letter) dead.exchange ──▶ delay.queue ──▶ Consumer
1 2 3 4 5 6 7 8 @Bean public Queue ttlQueue () { return QueueBuilder.durable("ttl.queue" ) .withArgument("x-dead-letter-exchange" , "dead.exchange" ) .withArgument("x-dead-letter-routing-key" , "delay.key" ) .withArgument("x-message-ttl" , 5000 ) .build(); }
Solution 2: Delayed Message Plugin (Recommended) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Bean public Exchange delayExchange () { return ExchangeBuilder .directExchange("delay.exchange" ) .delayed() .durable(true ) .build(); } rabbitTemplate.convertAndSend( "delay.exchange" , "delay.key" , message, msg -> { msg.getMessageProperties().setDelay(30000 ); return msg; } );
Best Practices & Common Issues 1. Message Backlog Handling
Emergency Solutions
Long-term Solutions
Temporarily scale up consumers
Plan consumer capacity properly
Optimize consumption logic (batch)
Use lazy queues
Limit production rate
Reduce synchronous code
2. Common Interview Questions Q: How to ensure message reliability?
Producer: Enable Confirm mechanism
MQ: Enable persistence (exchange, queue, message)
Consumer: Enable manual ack or auto-retry
Q: How to handle duplicate consumption?
Message ID deduplication
Business state check (recommended)
Q: How to design order timeout cancellation?
Use delayed message plugin
Multi-level delay checks (avoid resource waste)
3. Configuration Cheat Sheet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 spring: rabbitmq: host: localhost port: 5672 virtual-host: / username: guest password: guest publisher-confirm-type: correlated publisher-returns: true listener: simple: prefetch: 1 acknowledge-mode: auto retry: enabled: true initial-interval: 1000ms max-attempts: 3
Summary RabbitMQ is a mature message middleware with excellent performance in reliability, usability, and functionality. Key takeaways from this article:
Selection : For small to medium scale scenarios, prioritize RabbitMQ for its mature ecosystem and ease of use
Core Concepts : Exchanges handle routing, queues handle storage, bindings determine routing rules
Exchange Types : Fanout for broadcast, Direct for exact match, Topic for pattern matching
Reliability : Producer Confirm + MQ Persistence + Consumer Ack + Business Idempotency
Delayed Messages : Dead letter exchange and delayed plugin approaches
Proper use of message middleware can effectively improve system decoupling, scalability, and stability.
References