Implement asynchronous communication between UserService
, ProductService
, and OrderService
using RabbitMQ. This setup will allow microservices to exchange messages efficiently, improving decoupling and scalability.
- Open
docker-compose.yml
in the MicroservicesProject directory. - Add the following configuration for RabbitMQ:
rabbitmq: image: rabbitmq:3-management ports: - "5672:5672" # RabbitMQ server port - "15672:15672" # Management console port environment: - RABBITMQ_DEFAULT_USER=guest - RABBITMQ_DEFAULT_PASS=guest
- In each service (
UserService
,ProductService
, andOrderService
), openpom.xml
. - Add the Spring Boot starter for RabbitMQ:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
- In each service’s
application.properties
, add the RabbitMQ connection details:spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest
- In each service, create a
Message
class to represent the message structure:public class OrderMessage { private Long orderId; private Long userId; private Long productId; private int quantity; // Getters and Setters }
- In
OrderService
, create aMessageProducer
class to publish messages to RabbitMQ:@Service public class MessageProducer { private final RabbitTemplate rabbitTemplate; @Autowired public MessageProducer(RabbitTemplate rabbitTemplate) { this.rabbitTemplate = rabbitTemplate; } public void sendOrderMessage(OrderMessage orderMessage) { rabbitTemplate.convertAndSend("order.exchange", "order.routingKey", orderMessage); } }
- In
OrderService
, create aRabbitMQConfig
class to define the exchange, queue, and routing key:@Configuration public class RabbitMQConfig { @Bean public TopicExchange exchange() { return new TopicExchange("order.exchange"); } @Bean public Queue queue() { return new Queue("order.queue"); } @Bean public Binding binding(Queue queue, TopicExchange exchange) { return BindingBuilder.bind(queue).to(exchange).with("order.routingKey"); } }
- In the order creation method, publish an order message to RabbitMQ:
@Autowired private MessageProducer messageProducer; public Order createOrder(Order order) { Order savedOrder = orderRepository.save(order); OrderMessage orderMessage = new OrderMessage(savedOrder.getId(), savedOrder.getUserId(), savedOrder.getProductId(), savedOrder.getQuantity()); messageProducer.sendOrderMessage(orderMessage); return savedOrder; }
- In
ProductService
, create aMessageConsumer
class to listen for order messages:@Service public class MessageConsumer { @RabbitListener(queues = "order.queue") public void receiveOrderMessage(OrderMessage orderMessage) { System.out.println("Received order: " + orderMessage); // Process the order (e.g., update product stock) } }
- Start all services and RabbitMQ with Docker Compose:
docker-compose up -d
- Use Postman to send a request to
OrderService
to create an order. Verify that the message is sent toProductService
.
- Access the RabbitMQ management console at
http://localhost:15672
. - Log in with username
guest
and passwordguest
. - Go to the Queues tab to monitor messages in
order.queue
.
- In
MessageConsumer
, add error handling logic to retry message processing in case of errors:@RabbitListener(queues = "order.queue") public void receiveOrderMessage(OrderMessage orderMessage) { try { // Process message } catch (Exception e) { System.out.println("Error processing message, will retry."); throw e; // Message will be requeued } }
- Review what was covered:
- Configured RabbitMQ for asynchronous messaging.
- Set up message producers and consumers for
OrderService
andProductService
. - Verified message flow and monitored RabbitMQ queues.
-
Implement a New Queue and Message Type:
- Create a new queue (e.g.,
user.queue
) forUserService
to listen for messages related to user actions. - Test sending and receiving user-related messages in RabbitMQ.
- Create a new queue (e.g.,
-
Implement Delayed Messaging:
- Configure a delay in message processing for specific message types.
- Test that messages are processed after a delay and verify the behavior in the RabbitMQ console.
-
Monitor and Handle Dead Letter Messages:
- Set up a dead-letter queue for messages that fail to be processed after multiple attempts.
- Send messages that intentionally cause an error and verify that they are sent to the dead-letter queue.