Spring Boot Messaging with RabbitMQ

Spring Boot Messaging with RabbitMQ

RabbitMQ is an open-source message broker that allows communication between different services of enterprise applications. It is typically used in microservices to send and receive messages for inter-service communication.

Messaging In RabbitMQ involves:

  • A producer is a user application that sends messages to a RabbitMQ message broker. Messages are not directly sent to a queue. Here, the producer sends messages to an exchange. Exchanges are message routing agents that are responsible for routing the message to different queues.
  • A queue is a buffer that resides inside RabbitMQ to store messages that a producer sends and a receiver receives.
  • A consumer is a user application that receives messages from the RabbitMQ message broker and then processes them further.

This image shows how messages are communicated in RabbitMQ.

No alt text provided for this image
No alt text provided for this image

The Application

We will create a Spring Boot multi-module project in order to perform messaging with RabbitMQ.

The application consists of two services:

  • Producer Service that produces messages to RabbitMQ.
  • Consumer Service that consumes messages from RabbitMQ.

To use RabbitMQ in your application, you first need to add the dependency of RabbitMQ in your parent?

pom.xml file.The code to add the RabbitMQ dependency is this:

<dependency
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>>        

The application that I will create will contain two Spring Boot services: Producer Service and Consumer Service.

The Producer Service

The Producer Service is a Spring Boot RESTFul service that produces messages to RabbitMQ. In the Producer Service, we will create:

  • A domain object
  • The?application.yml?file
  • A service
  • A controller

The Domain Object

In the Producer Service application, let’s start by writing a?

User domain class whose objects we will exchange as messages. The code of the?

User class is this.


@Component
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id", scope = User.class)
public class User implements Serializable {

    private String userId;
    private String userName;

    public User(String userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }

    public User() {

    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                '}';
    }
}        

The?User class implements the Serializable interface as it will be transmitted over a messaging system. It is also annotated with the @Component annotation so that Spring treats it as a Spring-managed object. The?@JsonIdentityInfo annotation indicates the object identity during the serialization and deserialization process.

The application.yml File

Next, we will write the?application.yml file for the Producer Service. Here we will specify the configurations required by an application to connect with RabbitMQ.

app
  message: Message has been sent Successfully..
spring:
  rabbitmq:
    host: localhost
    password: guest
    port: 15672
    username: guest
    exchange: user.exchange
    queue: user.queue
    routingkey: user.routingkey
server:
  port: 9091:        

The Service Class

We will create a service class named?RabbitMQSender to send messages to RabbitMQ.The code of the?RabbitMQSender class is this.

@Service
public class RabbitMqSender {
    private RabbitTemplate rabbitTemplate;

    @Autowired
    public RabbitMqSender(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Value("${spring.rabbitmq.exchange}")
    private String exchange;

    @Value("${spring.rabbitmq.routingkey}")
    private String routingkey;

    public void send(User user){
        rabbitTemplate.convertAndSend(exchange,routingkey, user);

    }

}
        

This class defines two properties:?exchange?and?routingkey. The?exchange?property defines the RabbitMQ exchange that is responsible for routing the messages to different queues. Then the?routingkey?property defines how to route the messages to the queue depending on the exchange type

Next, line 5-7 autowires an?rabbitTemplate object of the RabbitTemplate class. The RabbitTemplate class allows sending and receiving messages with RabbitMQ.

Then, line 10-14?sets the?exchange and routingkey fields with the values from the application.yml file.

Finally, line 16-17 defines the?send() method that calls the convertAndSend() method of the RabbitTemplate class and sets exchange routing user to it. This convertAndSend() method then pushes the message to exchange with the specified routingkey.

The Controller

Next, we will write the controller named?ProducerController.

The code of the?ProducerController class is this.

@RestController
@RequestMapping(value = "/api/v1/")
public class ProducerController {


    private RabbitMqSender rabbitMqSender;
    @Autowired
    public ProducerController(RabbitMqSender rabbitMqSender) {
        this.rabbitMqSender = rabbitMqSender;
    }

    @Value("${app.message}")
    private String message;

    @PostMapping(value = "user")
    public String publishUserDetails(@RequestBody User user) {
        rabbitMqSender.send(user);
        return message;
    }
}        

As you can see that this code uses the?@RestController annotation that makes this class a RESTful web service.

In this code, line 2 adds the?@RequestMapping annotation to map the HTTP requests to handler methods.

Then, line 6-9 autowires a?RabbitMqSender object to send a message to RabbitMQ.

Next, line 12-13 creates a property named?message and then annotated with the @Value annotation to get the values defined in our application.yml file.

Next, line 15-16 creates a?publishUserDetails() method annotated with the @PostMapping annotation to map this handler method to a URL named user with the HTTP POST method .

Finally, line 17?calls the?send() method of the RabbitMqSender class accepts a User object to send it to RabbitMQ.

Refactoring the ProducerServiceApplication Class

Finally, we will refactor the?ProducerServiceApplication class that contains the main() method of the application.

The code of the?ProducerServiceApplication class after refactoring is this.


@SpringBootApplication
public class ProducerServiceApplication {

    @Value("${spring.rabbitmq.host}")
    String host;

    @Value("${spring.rabbitmq.username}")
    String username;

    @Value("${spring.rabbitmq.password}")
    String password;

    public static void main(String[] args) {
        SpringApplication.run(ProducerServiceApplication.class, args);
    }

    @Bean
    CachingConnectionFactory connectionFactory() {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(host);
        cachingConnectionFactory.setUsername(username);
        cachingConnectionFactory.setPassword(password);
        return cachingConnectionFactory;
    }

    @Bean
    public MessageConverter jsonMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(jsonMessageConverter());
        return rabbitTemplate;
    }
}        

This class defines three properties :?

host, username and password and annotated with the @Value annotation to read the values defined in the application.yml file.

In this code, you can see that line 17-22 creates a?CashingConnectionFactory bean initialized with localhost, username, and password.

Next, line 25-27 creates a?Jackson2JsonMessageConverter bean of type MeaasgeConverter to send the message in the JSON format.

Finally, line 30-34 finally creates a?RabbitTemplate bean initialized with ConnectionFactory. It then calls the setMessageConverter() method and setsJsonMessageConverter() to set the MessageConverter.

The Consumer Service

The Consumer Service is a Spring Boot RESTful service that receives messages from RabbitMQ. Here, in the Consumer Service we will create:

  • A configuration class
  • The?application.yml?file
  • A message receiver class

The Configuration Class

In the Consumer Service application, let’s start by writing a configuration class that retrieves values set in the?application.yml file and then configures a message listener, declare the queue, exchange, and then binds them together.

The code of the configuration class named?RabbitMQConfigis this.


@Configuration
public class RabbitMQConfig {

    @Value("${spring.rabbitmq.queue}")
    private String queue;

    @Value("${spring.rabbitmq.exchange}")
    private String exchange;

    @Value("${spring.rabbitmq.routingkey}")
    private String routingKey;

    @Value("${spring.rabbitmq.username}")
    private String username;

    @Value("${spring.rabbitmq.password}")
    private String password;

    @Value("${spring.rabbitmq.host}")
    private String host;

    @Bean
    Queue queue() {
        return new Queue(queue, true);
    }

    @Bean
    Exchange myExchange() {
        return ExchangeBuilder.directExchange(exchange).durable(true).build();
    }

    @Bean
    Binding binding() {
        return BindingBuilder
                .bind(queue())
                .to(myExchange())
                .with(routingKey)
                .noargs();
    }

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(host);
        cachingConnectionFactory.setUsername(username);
        cachingConnectionFactory.setPassword(password);
        return cachingConnectionFactory;
    }

    @Bean
    public MessageConverter jsonMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(jsonMessageConverter());
        return rabbitTemplate;
    }
}        

This code defines six properties:?queue, exchange, routingKey, username, password and host. It?also uses the @Value annotation to read the values from the application.yml file.

This code also creates a?Queue bean that is durable.

In this code,?line 27-29 creates an?ExchangeBuilder bean to send a message to the queue whose binding key matches the routing key of the message.

Line 32-38 then creates a?Binding bean by binding the queue, exchange and routingkey.

Next, line 41-46 creates a?CashingConnectionFactory bean of type ConnectionFactory initialized with localhost, username, and password.

Then line 49-51 creates a?Jackson2JsonMessageConverter bean of type MeaasgeConverter to send the message in the JSON format.

Finally, line 54-58 creates a?RabbitTemplate bean initialized with ConnectionFactory. It then calls the setMessageConverter() method and sets JsonMessageConverter() to set the MessageConverter.

The application.yml File

Next, I will write the?application.yml file for the Consumer Service.

The code of the?application.yml file is this.

server:
  port: 8080
spring:
  rabbitmq:
    host: localhost
    password: guest
    port: 15672
    username: guest
    exchange: user.exchange
    queue: user.queue
    routingkey: user.routingkey
        

The Message Receiver Class

Let’s now write the?RabbitMqService class to receive messages as a Spring component.

The code of the?RabbitMqService class is this.

@Component
public class RabbitMqReceiver implements RabbitListenerConfigurer {

    private static final Logger logger = LoggerFactory.getLogger(RabbitMqReceiver.class);

    @Override
    public void configureRabbitListeners(RabbitListenerEndpointRegistrar rabbitListenerEndpointRegistrar) {

    }

    @RabbitListener(queues = "${spring.rabbitmq.queue}")
    public void receivedMessage(User user) {

        logger.info("User Details Received is.. " + user);
    }

}
        

This class implements the?RabbitListenerConfigurer interface that allows defining how listener endpoints are configured.

This class also overrides the configureRabbitListeners() method but without any implementation. Then creates a?receivedMessage() method that receives a User object from RabbitMQ. This method is annotated with the @RabbitListener annotation that defines the queue to consume messages.

Testing Messaging with RabbitMQ

Now, before you run the Producer Service and Consumer Service microservices, you need to start the RabbitMQ server.

To start the RabbitMQ server, in the terminal window type:

service rabbitmq-server start        

This command starts the RabbitMQ server and you can also check the status of the server with the following command:

service rabbitmq-server status        

The terminal window displays that the RabbitMQ server is already running, as you can see.

No alt text provided for this image

Let’s go back to the microservices and run the Producer Service and Consumer Service respectively.

To test the flow of messaging with RabbitMQ, you can use?Postman.

  1. In Postman, select?POST?from the drop-down list.
  2. Then type the?localhost:9091/api/v1/user?URL.
  3. Click the?Body?tab.
  4. Select the?raw?radio button and then select?JSON?from the drop-down list.
  5. Type?{ "userId": "1","userName": "Mark" }?to pass a JSON object.
  6. Click the?Send?button.

Postman displays a message indicating that the message has been successfully sent, as you can see in the figure.

No alt text provided for this image

Now, you can check the Console of the Consumer Service.

No alt text provided for this image

In the Console window, you can see the user details that have been received by the Consumer Service.

要查看或添加评论,请登录

Ahmed El-Sayed的更多文章

  • Open System Architecture

    Open System Architecture

    "Open System Architecture" typically refers to the design and implementation of systems using open standards, open…

  • ChatGPT: Revolutionizing Conversational AI

    ChatGPT: Revolutionizing Conversational AI

    Artificial intelligence (AI) has come a long way in recent years, and one of the most exciting developments in this…

  • Togaf 9.2 Level 1 (Part 1)

    Togaf 9.2 Level 1 (Part 1)

    efore we go in details , we have to know how can we read the open group standards document, you should download the…

    1 条评论
  • Kafka vs RabbitMQ

    Kafka vs RabbitMQ

    What’s the Difference Between a Message Broker and a Publish/Subscribe (Pub/Sub) Messaging System? Message brokers are…

  • What is the strangler pattern and how does it work?

    What is the strangler pattern and how does it work?

    What is the strangler pattern? Picture a motorcycle that works, but could stand to undergo extensive overhauls that…

  • MIGRATING FROM MONOLITH TO MICROSERVICES: STRATEGY & STEP-BY-STEP GUIDE

    MIGRATING FROM MONOLITH TO MICROSERVICES: STRATEGY & STEP-BY-STEP GUIDE

    Migrating from monolith to microservices is less costly and risky than redeveloping an entire system from scratch. But…

    1 条评论
  • Migrate a monolith application to microservices using DDD

    Migrate a monolith application to microservices using DDD

    A monolithic application is typically an application system in which all of the relevant modules are packaged together…

    1 条评论
  • Migrate From Monolithic To Microservices Using DDD Pattern

    Migrate From Monolithic To Microservices Using DDD Pattern

    The general migration approach has three steps: Stop adding functionality to the monolithic application Split the…

  • Migrate From Monolithic To Microservices Using Strangler Pattern

    Migrate From Monolithic To Microservices Using Strangler Pattern

    There are three steps to transition from a monolithic application to microservices by implementing the Strangler…

  • GraalVM

    GraalVM

    GraalVM has caused a revolution in Java development since it launched three years ago. One of the most discussed…

社区洞察

其他会员也浏览了