Spring Boot Microservices with Spring Cloud Stream Example

In this tutorial, we will create two Spring Boot microservices that communicate with each other using Spring Cloud Stream and RabbitMQ. Spring Cloud Stream is a framework for building message-driven microservices. This guide is intended for beginners and includes detailed explanations for each step.

Introduction to Spring Cloud Stream

Spring Cloud Stream is a framework for building highly scalable, event-driven microservices connected with shared messaging systems. It provides a flexible programming model built on the concepts of Spring Boot, including automatic configuration and binding to external messaging systems.

Prerequisites

  • JDK 17 or later
  • Maven or Gradle
  • RabbitMQ server (You can run RabbitMQ using Docker)
  • IDE (IntelliJ IDEA, Eclipse, etc.)

Step 1: Set Up RabbitMQ Server

You can run RabbitMQ using Docker with the following command:

docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

This command will start RabbitMQ with the management UI accessible at http://localhost:15672 (default username and password: guest/guest).

Step 2: Create the Projects

We'll create two Spring Boot projects: producer-service and consumer-service.

Step 3: Set Up producer-service

3.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web
  • Spring Boot Actuator
  • Spring Cloud Stream
  • Spring Cloud Stream RabbitMQ Binder

3.2 Configure application.properties

Set up the application properties for producer-service.

spring.application.name=producer-service
server.port=8081

# RabbitMQ settings
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

# Spring Cloud Stream settings
spring.cloud.stream.bindings.output.destination=exampleTopic
spring.cloud.stream.rabbit.bindings.output.producer.routing-key-expression='example'
spring.cloud.stream.default-binder=rabbit

Explanation:

  • spring.application.name=producer-service: Names the application.
  • server.port=8081: Sets the port for the producer-service.
  • spring.rabbitmq.*: Configures RabbitMQ connection settings.
  • spring.cloud.stream.bindings.output.destination=exampleTopic: Sets the destination topic for the output binding.
  • spring.cloud.stream.rabbit.bindings.output.producer.routing-key-expression='example': Sets the routing key expression for RabbitMQ.
  • spring.cloud.stream.default-binder=rabbit: Sets RabbitMQ as the default binder.

3.3 Create a Message Producer

Create a service to send messages to the stream.

package com.example.producerservice;

import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.stereotype.Service;

@Service
public class MessageProducer {

    private final StreamBridge streamBridge;

    public MessageProducer(StreamBridge streamBridge) {
        this.streamBridge = streamBridge;
    }

    public void sendMessage(String message) {
        streamBridge.send("output", message);
    }
}

Explanation:

  • @Service: Marks this class as a service component.
  • StreamBridge streamBridge: Injects the StreamBridge for sending messages to the stream.
  • sendMessage(String message): Sends a message to the output binding.

3.4 Create a Controller

Create a controller to handle HTTP requests and send messages to the stream.

package com.example.producerservice;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MessageController {

    private final MessageProducer messageProducer;

    public MessageController(MessageProducer messageProducer) {
        this.messageProducer = messageProducer;
    }

    @GetMapping("/send")
    public String sendMessage(@RequestParam String message) {
        messageProducer.sendMessage(message);
        return "Message sent: " + message;
    }
}

Explanation:

  • @RestController: Marks this class as a REST controller.
  • @GetMapping("/send"): Maps GET requests to /send to this method.
  • @RequestParam String message: Extracts the message parameter from the request.
  • messageProducer.sendMessage(message): Sends the message to the stream.

Step 4: Set Up consumer-service

4.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web
  • Spring Boot Actuator
  • Spring Cloud Stream
  • Spring Cloud Stream RabbitMQ Binder

4.2 Configure application.properties

Set up the application properties for consumer-service.

spring.application.name=consumer-service
server.port=8082

# RabbitMQ settings
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

# Spring Cloud Stream settings
spring.cloud.stream.bindings.input.destination=exampleTopic
spring.cloud.stream.rabbit.bindings.input.consumer.binding-routing-key=example
spring.cloud.stream.default-binder=rabbit

Explanation:

  • spring.application.name=consumer-service: Names the application.
  • server.port=8082: Sets the port for the consumer-service.
  • spring.rabbitmq.*: Configures RabbitMQ connection settings.
  • spring.cloud.stream.bindings.input.destination=exampleTopic: Sets the destination topic for the input binding.
  • spring.cloud.stream.rabbit.bindings.input.consumer.binding-routing-key=example: Sets the routing key for RabbitMQ.
  • spring.cloud.stream.default-binder=rabbit: Sets RabbitMQ as the default binder.

4.3 Create a Message Listener

Create a service to listen to messages from the stream.

package com.example.consumerservice;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.util.function.Consumer;

@Service
public class MessageListener {

    @Bean
    public Consumer<String> input() {
        return message -> {
            System.out.println("Received message: " + message);
        };
    }
}

Explanation:

  • @Service: Marks this class as a service component.
  • @Bean: Marks this method as a bean producer.
  • Consumer<String> input(): Defines a consumer that listens to the input binding and processes received messages.

Step 5: Run the Microservices

  1. Start RabbitMQ: Ensure RabbitMQ is running using the Docker command mentioned above.
  2. Start producer-service: Run the ProducerServiceApplication class.
  3. Start consumer-service: Run the ConsumerServiceApplication class.

Step 6: Test the Communication

  1. Open your browser or use a tool like Postman to send a GET request to producer-service:

    • URL: http://localhost:8081/send?message=Hello
    • This will send the message "Hello" to the stream.
  2. Check the console logs of consumer-service to see the received message:

    • You should see Received message: Hello in the logs.

Conclusion

You have successfully set up two Spring Boot microservices that communicate asynchronously using Spring Cloud Stream and RabbitMQ. This setup allows you to build scalable and event-driven microservices architecture with ease. This example can be expanded to include more complex message handling, additional microservices, and advanced Spring Cloud Stream configurations.


Comments