Messaging Application using Rabbit MQ, Spring Boot, and Spring AMQP
Messaging is a secure and reliable way of exchanging information between multiple parties. In this tutorial, we will create a simple data exchange mechanism using Spring Boot, the Spring AMQP protocol, and RabbitMQ.
AMQP Protocol
AMQP provides a standardized way to send and receive messages, defining a set of patterns and an architecture that can be used for various messaging scenarios, regardless of the platform or technologies used. It also ensures reliable delivery of messages, maintaining the correct order and preventing message loss. Furthermore, AMQP provides secure communication between messaging systems. It can be implemented with messaging systems like RabbitMQ, Microsoft Azure Service Bus, and Apache Qpid, and is supported by languages such as Java, Python, and .NET.
AMQP Architecture
1. Environment setup
1.1. Create a Rabbit MQ server locally using docker
- To get started, download the Docker Desktop application from https://www.docker.com/products/docker-desktop/.
- Next, go to https://hub.docker.com/ and search for the RabbitMQ image. Copy the name of an image that includes a management tag
- To pull the Docker image using the terminal, use the following command, replacing ‘3.10.5-management’ with the image name you selected:
docker pull rabbitmq:3.10.5-management
It will pull the docker image and you can verify it by going to the image tab in the docker desktop application.
- Then you have to start the docker container. Use the following command
docker run --rm -it -p 15672:15672 -p 5672:5672 rabbitmq:3.10.5-management
- You have now started a local RabbitMQ server. You can verify it by navigating to the RabbitMQ management console, which is up and running on port 15672. To access the console, go to http://localhost:15672/ in your web browser.
- Default UserName and Password is guest
2. Setup Spring boot project
- Download a project using https://start.spring.io/
- Add the following Dependencies
3. Creating the Message Producer class
application.properties
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
rabbitmq.queue.name =simple_messaging_app
rabbitmq.exchange.name = simple-messaging-app_exchange
rabbitmq.routing.key = simple-messaging-app_routing_key
Now we need to create the configuration class to bind the queue with the relevant exchanges. Let’s create a class named RabbitMQConfig.java
package com.javaMqGuide.springbootrabbitMqproject.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
@Value("${rabbitmq.queue.name}")
//${rabbitmq.queue.name} will get fetched from the application.properties
private String queue;
@Value("${rabbitmq.exchange.name}")
private String exchange;
@Value("${rabbitmq.routing.key}")
private String routingkey;
@Bean
public Queue queue(){
return new Queue(queue);
}
@Bean
public TopicExchange exchange(){
return new TopicExchange(exchange);
}
//binding between queue and exchange using routing key
@Bean
public Binding binding(){
return BindingBuilder.bind(queue())
.to(exchange())
.with(routingkey);
}
}
In the RabbitMQConfig class, we will create separate beans to return a queue and an exchange. The messaging queue will be bound to the exchange using a routing key.
Creating the Message Producer
RabbitMQProduser.java
package com.javaMqGuide.springbootrabbitMqproject.publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class RabbitMQProducer {
@Value("${rabbitmq.exchange.name}")
private String exchange;
@Value("${rabbitmq.routing.key}")
private String routingkey;
private RabbitTemplate rabbitMqTemplate;
@Autowired
public RabbitMQProduser(RabbitTemplate rabbitMqTemplate) {
this.rabbitMqTemplate = rabbitMqTemplate;
}
public void sendMessage(String message){
rabbitMqTemplate.convertAndSend(exchange,routingkey,message);
}
}
- {rabbitmq.routing.key} and {rabbitmq.exchange.name} will be fetched from the application.properties file
Message Producer is responsible for publishing a message to the queue.
Now, let’s create a REST controller to publish messages. This will create a REST endpoint that uses the RabbitMQProducer class to publish the messages. Let’s create a class named MessageController.java.
package com.javaMqGuide.springbootrabbitMqproject.controller;
import com.javaMqGuide.springbootrabbitMqproject.publisher.RabbitMQProduser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1")
public class MessageController {
@Autowired
private RabbitMQProduser produser;
@GetMapping("/publish")
public ResponseEntity<String> sendMessage(@RequestParam("message") String message){
produser.sendMessage(message);
return ResponseEntity.ok("Message send successfullt to RabbitMQ");
}
}
Now you can use http://localhost:8080/api/v1/publish?message=Test Message url to publish a message to the queue
To verify that the implementation is working as intended, use the RabbitMQ management console. You should see the message get queued from the console.
4. Creating the message consumer class
package com.javaMqGuide.springbootrabbitMqproject.consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@Service
public class RabbitMQConcumer {
private static final Logger LOGGER = LoggerFactory.getLogger(RabbitMQConcumer.class);
@RabbitListener(queues = {"${rabbitmq.queue.name}"})
public void consume(String message){
LOGGER.info(String.format("Received mesage -> %s",message));
}
}
We now need to create a listener for the queue. When a message is published to the queue, the listener will be executed. You can use a logger instance to test the consumer.